diff --git a/app-commons-test/src/test/scala/org/bitcoins/commons/DLCStatusTest.scala b/app-commons-test/src/test/scala/org/bitcoins/commons/DLCStatusTest.scala index d5776f9f69..fbb8233def 100644 --- a/app-commons-test/src/test/scala/org/bitcoins/commons/DLCStatusTest.scala +++ b/app-commons-test/src/test/scala/org/bitcoins/commons/DLCStatusTest.scala @@ -29,6 +29,8 @@ class DLCStatusTest extends BitcoinSJvmTest { val payoutAddress = Option.empty[PayoutAddress] + val contact = Some("127.0.0.1:0") + val status = DLCStatus.Offered( Sha256Digest.empty, @@ -40,7 +42,8 @@ class DLCStatusTest extends BitcoinSJvmTest { offer.feeRate, totalCollateral, offer.collateral, - payoutAddress + payoutAddress, + contact ) assert(status.state == DLCState.Offered) @@ -66,6 +69,8 @@ class DLCStatusTest extends BitcoinSJvmTest { "tb1q4ps6c9ewa7uca5v39fakykq9q6hpgjkxje8gve"), true)) + val contact = Option.empty[String] + val status = DLCStatus.Accepted( Sha256Digest.empty, @@ -78,7 +83,8 @@ class DLCStatusTest extends BitcoinSJvmTest { offer.feeRate, totalCollateral, offer.collateral, - payoutAddress + payoutAddress, + contact ) assert(status.state == DLCState.Accepted) @@ -100,6 +106,8 @@ class DLCStatusTest extends BitcoinSJvmTest { val payoutAddress = Option.empty[PayoutAddress] + val contact = Option.empty[String] + val status = DLCStatus.Signed( Sha256Digest.empty, @@ -113,7 +121,8 @@ class DLCStatusTest extends BitcoinSJvmTest { totalCollateral, offer.collateral, txId, - payoutAddress + payoutAddress, + contact ) assert(status.state == DLCState.Signed) @@ -135,6 +144,8 @@ class DLCStatusTest extends BitcoinSJvmTest { val payoutAddress = Option.empty[PayoutAddress] + val contact = Option.empty[String] + val status = DLCStatus.Broadcasted( Sha256Digest.empty, @@ -148,7 +159,8 @@ class DLCStatusTest extends BitcoinSJvmTest { totalCollateral, offer.collateral, fundingTxId, - payoutAddress + payoutAddress, + contact ) assert(status.state == DLCState.Broadcasted) @@ -170,6 +182,8 @@ class DLCStatusTest extends BitcoinSJvmTest { val payoutAddress = Option.empty[PayoutAddress] + val contact = Option.empty[String] + val status = DLCStatus.Confirmed( Sha256Digest.empty, @@ -183,7 +197,8 @@ class DLCStatusTest extends BitcoinSJvmTest { totalCollateral, offer.collateral, fundingTxId, - payoutAddress + payoutAddress, + contact ) assert(status.state == DLCState.Confirmed) @@ -216,6 +231,8 @@ class DLCStatusTest extends BitcoinSJvmTest { val payoutAddress = Option.empty[PayoutAddress] + val contact = Option.empty[String] + val status = DLCStatus.Claimed( Sha256Digest.empty, @@ -234,7 +251,8 @@ class DLCStatusTest extends BitcoinSJvmTest { outcome, myPayout = myPayout, counterPartyPayout = theirPayout, - payoutAddress = payoutAddress + payoutAddress = payoutAddress, + contact ) assert(status.state == DLCState.Claimed) @@ -270,6 +288,8 @@ class DLCStatusTest extends BitcoinSJvmTest { val payoutAddress = Option.empty[PayoutAddress] + val contact = Option.empty[String] + val status = DLCStatus.RemoteClaimed( Sha256Digest.empty, @@ -288,7 +308,8 @@ class DLCStatusTest extends BitcoinSJvmTest { outcome, myPayout = myPayout, counterPartyPayout = theirPayout, - payoutAddress = payoutAddress + payoutAddress = payoutAddress, + contact ) assert(status.state == DLCState.RemoteClaimed) @@ -320,6 +341,8 @@ class DLCStatusTest extends BitcoinSJvmTest { val payoutAddress = Option.empty[PayoutAddress] + val contact = Option.empty[String] + val status = DLCStatus.Refunded( Sha256Digest.empty, @@ -336,7 +359,8 @@ class DLCStatusTest extends BitcoinSJvmTest { closingTxId, myPayout = myPayout, counterPartyPayout = theirPayout, - payoutAddress = payoutAddress + payoutAddress = payoutAddress, + contact ) assert(status.state == DLCState.Refunded) diff --git a/app-commons/src/main/scala/org/bitcoins/commons/rpc/Command.scala b/app-commons/src/main/scala/org/bitcoins/commons/rpc/Command.scala index 359e0f7ea4..05c7f9514e 100644 --- a/app-commons/src/main/scala/org/bitcoins/commons/rpc/Command.scala +++ b/app-commons/src/main/scala/org/bitcoins/commons/rpc/Command.scala @@ -1,6 +1,7 @@ package org.bitcoins.commons.rpc import org.bitcoins.core.api.dlc.wallet.db.DLCContactDb +import org.bitcoins.crypto.Sha256Digest import java.net.{InetSocketAddress, URI} import scala.util.{Failure, Try} @@ -84,3 +85,53 @@ object ContactRemove { } } } + +case class DLCContactAdd(dlcId: Sha256Digest, address: InetSocketAddress) + extends CommandRpc + with AppServerCliCommand + +object DLCContactAdd { + + val empty: DLCContactAdd = + DLCContactAdd(Sha256Digest.empty, + InetSocketAddress.createUnresolved("127.0.0.1", 9999)) + + def fromJsArr(arr: ujson.Arr): Try[DLCContactAdd] = { + arr.arr.toList match { + case dlcIdJs :: addressJs :: Nil => + Try { + val dlcId = Sha256Digest.fromHex(dlcIdJs.str) + val address = { + val uri = new URI(s"tcp://${addressJs.str}") + InetSocketAddress.createUnresolved(uri.getHost, uri.getPort) + } + DLCContactAdd(dlcId, address) + } + case other => + val exn = new IllegalArgumentException( + s"Bad number or arguments to dlc-contact-add, got=${other.length} expected=2") + Failure(exn) + } + } +} + +case class DLCContactRemove(dlcId: Sha256Digest) + extends CommandRpc + with AppServerCliCommand + +object DLCContactRemove { + + def fromJsArr(arr: ujson.Arr): Try[DLCContactRemove] = { + arr.arr.toList match { + case dlcIdJs :: Nil => + Try { + val dlcId = Sha256Digest.fromHex(dlcIdJs.str) + DLCContactRemove(dlcId) + } + case other => + val exn = new IllegalArgumentException( + s"Bad number or arguments to contact-remove, got=${other.length} expected=1") + Failure(exn) + } + } +} diff --git a/app-commons/src/main/scala/org/bitcoins/commons/serializers/Picklers.scala b/app-commons/src/main/scala/org/bitcoins/commons/serializers/Picklers.scala index 29d314489d..2c9dd93d7e 100644 --- a/app-commons/src/main/scala/org/bitcoins/commons/serializers/Picklers.scala +++ b/app-commons/src/main/scala/org/bitcoins/commons/serializers/Picklers.scala @@ -6,6 +6,7 @@ import org.bitcoins.commons.serializers.JsonReaders.jsToSatoshis import org.bitcoins.core.api.dlc.wallet.db.{DLCContactDb, IncomingDLCOfferDb} import org.bitcoins.core.api.wallet.CoinSelectionAlgo import org.bitcoins.core.api.wallet.db.SpendingInfoDb +import org.bitcoins.core.config.DLC import org.bitcoins.core.crypto._ import org.bitcoins.core.currency.{Bitcoins, Satoshis} import org.bitcoins.core.dlc.accounting.DLCWalletAccounting @@ -815,7 +816,13 @@ object Picklers { writer[Value].comap { payoutAddressOpt => payoutAddressOpt .map(pa => writeJs(pa)) - .getOrElse(Null) + .getOrElse(ujson.Null) + } + + implicit val optionContactDbW: Writer[Option[DLCContactDb]] = + writer[Value].comap { + case Some(contact) => writeContactDb(contact) + case None => ujson.Null } implicit val offeredW: Writer[Offered] = @@ -836,7 +843,8 @@ object Picklers { "totalCollateral" -> Num(totalCollateral.satoshis.toLong.toDouble), "localCollateral" -> Num(localCollateral.satoshis.toLong.toDouble), "remoteCollateral" -> Num(remoteCollateral.satoshis.toLong.toDouble), - "payoutAddress" -> writeJs(payoutAddress) + "payoutAddress" -> writeJs(payoutAddress), + "peer" -> peer.map(p => writeJs(p)).getOrElse(Null) ) } @@ -859,7 +867,8 @@ object Picklers { "totalCollateral" -> Num(totalCollateral.satoshis.toLong.toDouble), "localCollateral" -> Num(localCollateral.satoshis.toLong.toDouble), "remoteCollateral" -> Num(remoteCollateral.satoshis.toLong.toDouble), - "payoutAddress" -> writeJs(payoutAddress) + "payoutAddress" -> writeJs(payoutAddress), + "peer" -> peer.map(p => writeJs(p)).getOrElse(Null) ) } @@ -881,7 +890,8 @@ object Picklers { "totalCollateral" -> Num(totalCollateral.satoshis.toLong.toDouble), "localCollateral" -> Num(localCollateral.satoshis.toLong.toDouble), "remoteCollateral" -> Num(remoteCollateral.satoshis.toLong.toDouble), - "payoutAddress" -> writeJs(payoutAddress) + "payoutAddress" -> writeJs(payoutAddress), + "peer" -> peer.map(p => writeJs(p)).getOrElse(Null) ) } @@ -905,7 +915,8 @@ object Picklers { "localCollateral" -> Num(localCollateral.satoshis.toLong.toDouble), "remoteCollateral" -> Num(remoteCollateral.satoshis.toLong.toDouble), "fundingTxId" -> Str(fundingTxId.hex), - "payoutAddress" -> writeJs(payoutAddress) + "payoutAddress" -> writeJs(payoutAddress), + "peer" -> peer.map(p => writeJs(p)).getOrElse(Null) ) } @@ -928,7 +939,8 @@ object Picklers { "localCollateral" -> Num(localCollateral.satoshis.toLong.toDouble), "remoteCollateral" -> Num(remoteCollateral.satoshis.toLong.toDouble), "fundingTxId" -> Str(fundingTxId.hex), - "payoutAddress" -> writeJs(payoutAddress) + "payoutAddress" -> writeJs(payoutAddress), + "peer" -> peer.map(p => writeJs(p)).getOrElse(Null) ) } @@ -952,7 +964,8 @@ object Picklers { "localCollateral" -> Num(localCollateral.satoshis.toLong.toDouble), "remoteCollateral" -> Num(remoteCollateral.satoshis.toLong.toDouble), "fundingTxId" -> Str(fundingTxId.hex), - "payoutAddress" -> writeJs(payoutAddress) + "payoutAddress" -> writeJs(payoutAddress), + "peer" -> peer.map(p => writeJs(p)).getOrElse(Null) ) } @@ -976,7 +989,8 @@ object Picklers { "localCollateral" -> Num(localCollateral.satoshis.toLong.toDouble), "remoteCollateral" -> Num(remoteCollateral.satoshis.toLong.toDouble), "fundingTxId" -> Str(fundingTxId.hex), - "payoutAddress" -> writeJs(payoutAddress) + "payoutAddress" -> writeJs(payoutAddress), + "peer" -> peer.map(p => writeJs(p)).getOrElse(Null) ) } @@ -1019,7 +1033,8 @@ object Picklers { claimed.counterPartyPayout.satoshis.toLong.toDouble), PicklerKeys.pnl -> Num(claimed.pnl.satoshis.toLong.toDouble), PicklerKeys.rateOfReturn -> Num(claimed.rateOfReturn.toDouble), - "payoutAddress" -> writeJs(payoutAddress) + "payoutAddress" -> writeJs(payoutAddress), + "peer" -> peer.map(p => writeJs(p)).getOrElse(Null) ) } @@ -1062,7 +1077,8 @@ object Picklers { remoteClaimed.counterPartyPayout.satoshis.toLong.toDouble), PicklerKeys.pnl -> Num(remoteClaimed.pnl.satoshis.toLong.toDouble), PicklerKeys.rateOfReturn -> Num(remoteClaimed.rateOfReturn.toDouble), - "payoutAddress" -> writeJs(payoutAddress) + "payoutAddress" -> writeJs(payoutAddress), + "peer" -> peer.map(p => writeJs(p)).getOrElse(Null) ) } @@ -1091,7 +1107,8 @@ object Picklers { refunded.counterPartyPayout.satoshis.toLong.toDouble), PicklerKeys.pnl -> Num(refunded.pnl.satoshis.toLong.toDouble), PicklerKeys.rateOfReturn -> Num(refunded.rateOfReturn.toDouble), - "payoutAddress" -> writeJs(payoutAddress) + "payoutAddress" -> writeJs(payoutAddress), + "peer" -> peer.map(p => writeJs(p)).getOrElse(Null) ) } @@ -1163,6 +1180,9 @@ object Picklers { val feeRate = SatoshisPerVirtualByte.fromLong(obj("feeRate").num.toLong) val totalCollateral = Satoshis(obj("totalCollateral").num.toLong) val localCollateral = Satoshis(obj("localCollateral").num.toLong) + val peerOpt = + if (obj("peer").isNull || !obj.value.contains("peer")) None + else Some(obj("peer").str) lazy val contractId = ByteVector.fromValidHex(obj("contractId").str) lazy val fundingTxId = DoubleSha256DigestBE(obj("fundingTxId").str) @@ -1232,7 +1252,8 @@ object Picklers { feeRate, totalCollateral, localCollateral, - payoutAddress + payoutAddress, + peerOpt ) case DLCState.AcceptComputingAdaptorSigs => AcceptedComputingAdaptorSigs( @@ -1246,7 +1267,8 @@ object Picklers { feeRate, totalCollateral, localCollateral, - payoutAddress + payoutAddress, + peerOpt ) case DLCState.Accepted => Accepted( @@ -1260,7 +1282,8 @@ object Picklers { feeRate, totalCollateral, localCollateral, - payoutAddress + payoutAddress, + peerOpt ) case DLCState.SignComputingAdaptorSigs => SignedComputingAdaptorSigs( @@ -1275,7 +1298,8 @@ object Picklers { totalCollateral, localCollateral, fundingTxId, - payoutAddress + payoutAddress, + peerOpt ) case DLCState.Signed => Signed( @@ -1290,7 +1314,8 @@ object Picklers { totalCollateral, localCollateral, fundingTxId, - payoutAddress + payoutAddress, + peerOpt ) case DLCState.Broadcasted => Broadcasted( @@ -1305,7 +1330,8 @@ object Picklers { totalCollateral, localCollateral, fundingTxId, - payoutAddress + payoutAddress, + peerOpt ) case DLCState.Confirmed => Confirmed( @@ -1320,7 +1346,8 @@ object Picklers { totalCollateral, localCollateral, fundingTxId, - payoutAddress + payoutAddress, + peerOpt ) case DLCState.Claimed => Claimed( @@ -1340,7 +1367,8 @@ object Picklers { oracleOutcome, myPayout = myPayoutOpt.get, counterPartyPayout = theirPayoutOpt.get, - payoutAddress + payoutAddress, + peerOpt ) case DLCState.RemoteClaimed => require(oracleSigs.size == 1, @@ -1362,7 +1390,8 @@ object Picklers { oracleOutcome, myPayout = myPayoutOpt.get, counterPartyPayout = theirPayoutOpt.get, - payoutAddress + payoutAddress, + peerOpt ) case DLCState.Refunded => Refunded( @@ -1380,7 +1409,8 @@ object Picklers { closingTxId, myPayout = myPayoutOpt.get, counterPartyPayout = theirPayoutOpt.get, - payoutAddress + payoutAddress, + peerOpt ) } } @@ -1523,7 +1553,7 @@ object Picklers { private def readContactDb(obj: ujson.Obj): DLCContactDb = { val addressStr = obj(PicklerKeys.addressKey).str val address: InetSocketAddress = - NetworkUtil.parseInetSocketAddress(addressStr, 2862) + NetworkUtil.parseInetSocketAddress(addressStr, DLC.DefaultPort) DLCContactDb( alias = obj(PicklerKeys.aliasKey).str, address = address, diff --git a/app/gui/src/main/scala/org/bitcoins/gui/WalletGUIModel.scala b/app/gui/src/main/scala/org/bitcoins/gui/WalletGUIModel.scala index 7f61ddf5f1..c61d561238 100644 --- a/app/gui/src/main/scala/org/bitcoins/gui/WalletGUIModel.scala +++ b/app/gui/src/main/scala/org/bitcoins/gui/WalletGUIModel.scala @@ -4,6 +4,7 @@ import akka.actor.{ActorSystem, Cancellable} import grizzled.slf4j.Logging import org.bitcoins.cli.CliCommand._ import org.bitcoins.cli.ConsoleCli +import org.bitcoins.core.config.DLC import org.bitcoins.core.dlc.accounting.RateOfReturnUtil import org.bitcoins.core.serializers.PicklerKeys import org.bitcoins.core.wallet.fee.FeeUnit @@ -175,7 +176,7 @@ class WalletGUIModel(dlcModel: DLCPaneModel)(implicit system: ActorSystem) } // Address returned from GetDLCHostAddress when Tor is disabled - private val DEFAULT_TOR_ADDRESS = "0:0:0:0:0:0:0:0:2862" + private val DEFAULT_TOR_ADDRESS = "0:0:0:0:0:0:0:0:" + DLC.DefaultPort /** Retrieves the tor endpoint address */ diff --git a/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/AcceptOfferDialog.scala b/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/AcceptOfferDialog.scala index 9574854578..47f6224421 100644 --- a/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/AcceptOfferDialog.scala +++ b/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/AcceptOfferDialog.scala @@ -1,6 +1,7 @@ package org.bitcoins.gui.dlc.dialog import org.bitcoins.cli.CliCommand._ +import org.bitcoins.core.config.DLC import org.bitcoins.core.protocol.dlc.models._ import org.bitcoins.core.protocol.tlv._ import org.bitcoins.core.util.NetworkUtil @@ -23,7 +24,7 @@ class AcceptOfferDialog extends CliCommandProducer[AcceptDLCCliCommand] { val offer = LnMessageFactory(DLCOfferTLV).fromHex(offerHex) val text = peerAddressTF.text.value.trim if (text.nonEmpty) { - val peer = NetworkUtil.parseInetSocketAddress(text, 2862) + val peer = NetworkUtil.parseInetSocketAddress(text, DLC.DefaultPort) AcceptDLC(offer, peer) } else { diff --git a/app/server-test/src/test/scala/org/bitcoins/server/DLCRoutesSpec.scala b/app/server-test/src/test/scala/org/bitcoins/server/DLCRoutesSpec.scala index 11aff2df2e..5e091a2fcc 100644 --- a/app/server-test/src/test/scala/org/bitcoins/server/DLCRoutesSpec.scala +++ b/app/server-test/src/test/scala/org/bitcoins/server/DLCRoutesSpec.scala @@ -7,6 +7,7 @@ import org.bitcoins.core.currency.{Bitcoins, Satoshis} import org.bitcoins.core.protocol.dlc.models.ContractInfo import org.bitcoins.core.protocol.tlv.OracleAnnouncementTLV import org.bitcoins.core.serializers.PicklerKeys +import org.bitcoins.crypto.Sha256Digest import org.bitcoins.dlc.node.DLCNode import org.bitcoins.server.routes.ServerCommand import org.bitcoins.testkit.BitcoinSTestAppConfig @@ -205,5 +206,43 @@ class DLCRoutesSpec assert(responseAs[String] == s"""{"result":"ok","error":null}""") } } + + "dlc-contact-add a peer" in { + (mockWallet + .addDLCContactMapping(_: Sha256Digest, _: InetSocketAddress)) + .expects(Sha256Digest.empty, expected.address) + .returning(Future.unit) + + val args = + ujson.Arr(ujson.Str(Sha256Digest.empty.hex), ujson.Str(address)) + + val route = + dlcRoutes.handleCommand(ServerCommand("dlc-contact-add", args)) + + Post() ~> route ~> check { + assert(contentType == ContentTypes.`application/json`) + assert( + responseAs[String] == s"""{"result":{"dlcId":"0000000000000000000000000000000000000000000000000000000000000000","contactId":"i3bfhauurptgypgnolqmj7xxv7pcs5c6udtzthbgojc7eaxx6zbbjyad.onion:2862"},"error":null}""") + } + } + + "dlc-contact-remove a peer" in { + + (mockWallet + .removeDLCContactMapping(_: Sha256Digest)) + .expects(Sha256Digest.empty) + .returning(Future.unit) + + val args = ujson.Arr(ujson.Str(Sha256Digest.empty.hex)) + + val route = + dlcRoutes.handleCommand(ServerCommand("dlc-contact-remove", args)) + + Post() ~> route ~> check { + assert(contentType == ContentTypes.`application/json`) + assert(responseAs[String] == s"""{"result":"ok","error":null}""") + } + } + } } diff --git a/app/server-test/src/test/scala/org/bitcoins/server/RoutesSpec.scala b/app/server-test/src/test/scala/org/bitcoins/server/RoutesSpec.scala index dc55bcfdda..ad96ebbf24 100644 --- a/app/server-test/src/test/scala/org/bitcoins/server/RoutesSpec.scala +++ b/app/server-test/src/test/scala/org/bitcoins/server/RoutesSpec.scala @@ -44,6 +44,7 @@ import org.scalatest.wordspec.AnyWordSpec import scodec.bits.ByteVector import ujson._ +import java.net.InetSocketAddress import java.time.{ZoneId, ZonedDateTime} import scala.collection.mutable import scala.concurrent.duration.DurationInt @@ -989,6 +990,7 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory { _: Option[SatoshisPerVirtualByte], _: UInt32, _: UInt32, + _: Option[InetSocketAddress], _: Option[BitcoinAddress], _: Option[BitcoinAddress] ) @@ -1000,6 +1002,7 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory { UInt32(contractMaturity), UInt32(contractTimeout), None, + None, None ) .returning(Future.successful(offer)) @@ -1059,9 +1062,10 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory { "accept a dlc offer" in { (mockWalletApi .acceptDLCOffer(_: DLCOfferTLV, + _: Option[InetSocketAddress], _: Option[BitcoinAddress], _: Option[BitcoinAddress])) - .expects(offer.toTLV, None, None) + .expects(offer.toTLV, None, None, None) .returning(Future.successful(accept)) val route = walletRoutes.handleCommand( diff --git a/app/server-test/src/test/scala/org/bitcoins/server/WalletRoutesSpec.scala b/app/server-test/src/test/scala/org/bitcoins/server/WalletRoutesSpec.scala index 247e1c10b7..09637cb727 100644 --- a/app/server-test/src/test/scala/org/bitcoins/server/WalletRoutesSpec.scala +++ b/app/server-test/src/test/scala/org/bitcoins/server/WalletRoutesSpec.scala @@ -75,7 +75,8 @@ class WalletRoutesSpec feeRate = null, totalCollateral = null, localCollateral = null, - payoutAddress = None + payoutAddress = None, + peer = None ) (mockWalletApi.findDLCByTemporaryContractId: Sha256Digest => Future[ diff --git a/app/server/src/main/scala/org/bitcoins/server/DLCRoutes.scala b/app/server/src/main/scala/org/bitcoins/server/DLCRoutes.scala index 2956387b91..6ef3e46cb7 100644 --- a/app/server/src/main/scala/org/bitcoins/server/DLCRoutes.scala +++ b/app/server/src/main/scala/org/bitcoins/server/DLCRoutes.scala @@ -3,7 +3,7 @@ package org.bitcoins.server import akka.actor.ActorSystem import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server._ -import org.bitcoins.commons.rpc.{ContactAdd, ContactRemove} +import org.bitcoins.commons.rpc._ import org.bitcoins.commons.serializers.Picklers import org.bitcoins.core.api.dlc.node.DLCNodeApi import org.bitcoins.core.api.dlc.wallet.db.IncomingDLCOfferDb @@ -159,5 +159,32 @@ case class DLCRoutes(dlcNode: DLCNodeApi)(implicit system: ActorSystem) } } + case ServerCommand("dlc-contact-add", arr) => + withValidServerCommand(DLCContactAdd.fromJsArr(arr)) { dlcContactAdd => + complete { + dlcNode.wallet + .addDLCContactMapping(dlcContactAdd.dlcId, dlcContactAdd.address) + .map { _ => + val dlcId = dlcContactAdd.dlcId.hex + val contactId = + dlcContactAdd.address.getHostName + ":" + dlcContactAdd.address.getPort + Server.httpSuccess( + ujson.Obj("dlcId" -> dlcId, "contactId" -> contactId)) + } + } + } + + case ServerCommand("dlc-contact-remove", arr) => + withValidServerCommand(DLCContactRemove.fromJsArr(arr)) { + dlcContactRemove => + complete { + dlcNode.wallet + .removeDLCContactMapping(dlcContactRemove.dlcId) + .map { _ => + Server.httpSuccess("ok") + } + } + } + } } diff --git a/app/server/src/main/scala/org/bitcoins/server/ServerJsonModels.scala b/app/server/src/main/scala/org/bitcoins/server/ServerJsonModels.scala index 0ffa92de69..ac7c560353 100644 --- a/app/server/src/main/scala/org/bitcoins/server/ServerJsonModels.scala +++ b/app/server/src/main/scala/org/bitcoins/server/ServerJsonModels.scala @@ -4,6 +4,7 @@ import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.LockUnspentOutputParamet import org.bitcoins.commons.jsonmodels.cli.ContractDescriptorParser import org.bitcoins.commons.serializers.JsonReaders import org.bitcoins.core.api.wallet.CoinSelectionAlgo +import org.bitcoins.core.config.DLC import org.bitcoins.core.crypto._ import org.bitcoins.core.currency.{Bitcoins, Satoshis} import org.bitcoins.core.hd.AddressType @@ -15,11 +16,9 @@ import org.bitcoins.core.protocol.tlv._ import org.bitcoins.core.protocol.transaction.{Transaction, TransactionOutPoint} import org.bitcoins.core.protocol.{BitcoinAddress, BlockStamp} import org.bitcoins.core.psbt.PSBT -import org.bitcoins.core.util.NetworkUtil import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte import org.bitcoins.core.wallet.utxo.AddressLabelTag import org.bitcoins.crypto._ -import org.bitcoins.server.GetDLC.nullToOpt import scodec.bits.ByteVector import ujson._ @@ -650,6 +649,27 @@ object SendToAddress extends ServerJsonModels { } +case class GetDLCs(contactId: Option[InetSocketAddress]) + +object GetDLCs extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[GetDLCs] = { + jsArr.arr.toList match { + case addressJs :: Nil => + Try { + val address = jsToInetSocketAddress(addressJs) + GetDLCs(Some(address)) + } + case Nil => + Try(GetDLCs(None)) + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 1")) + } + } +} + case class GetDLC(dlcId: Sha256Digest) object GetDLC extends ServerJsonModels { @@ -678,7 +698,8 @@ case class CreateDLCOffer( locktimeOpt: Option[UInt32], refundLocktime: UInt32, externalPayoutAddressOpt: Option[BitcoinAddress], - externalChangeAddressOpt: Option[BitcoinAddress]) + externalChangeAddressOpt: Option[BitcoinAddress], + peerAddressOpt: Option[InetSocketAddress]) object CreateDLCOffer extends ServerJsonModels { @@ -691,7 +712,8 @@ object CreateDLCOffer extends ServerJsonModels { locktimeJs: Value, refundLTJs: Value, payoutAddressJs: Value, - changeAddressJs: Value) = Try { + changeAddressJs: Value, + peerAddressJs: Value) = Try { val contractInfoTLV = jsToContractInfoTLV(contractInfoJs) val collateral = jsToSatoshis(collateralJs) val feeRate = jsToSatoshisPerVirtualByteOpt(feeRateOptJs) @@ -704,13 +726,17 @@ object CreateDLCOffer extends ServerJsonModels { val changeAddressJsOpt = nullToOpt(changeAddressJs) val changeAddressOpt = changeAddressJsOpt.map(js => jsToBitcoinAddress(js)) + val peerAddressJsOpt = nullToOpt(peerAddressJs) + val peerAddressOpt = peerAddressJsOpt.map(js => jsToInetSocketAddress(js)) + CreateDLCOffer(contractInfoTLV, collateral, feeRate, locktimeOpt, refundLT, payoutAddressOpt, - changeAddressOpt) + changeAddressOpt, + peerAddressOpt) } jsArr.arr.toList match { @@ -721,6 +747,7 @@ object CreateDLCOffer extends ServerJsonModels { locktimeJs, refundLTJs, Null, + Null, Null) case contractInfoJs :: collateralJs :: feeRateOptJs :: locktimeJs :: refundLTJs :: payoutAddressJs :: Nil => parseParameters(contractInfoJs, @@ -729,6 +756,7 @@ object CreateDLCOffer extends ServerJsonModels { locktimeJs, refundLTJs, payoutAddressJs, + Null, Null) case contractInfoJs :: collateralJs :: feeRateOptJs :: locktimeJs :: refundLTJs :: payoutAddressJs :: changeAddressJs :: Nil => parseParameters(contractInfoJs, @@ -737,7 +765,17 @@ object CreateDLCOffer extends ServerJsonModels { locktimeJs, refundLTJs, payoutAddressJs, - changeAddressJs) + changeAddressJs, + Null) + case contractInfoJs :: collateralJs :: feeRateOptJs :: locktimeJs :: refundLTJs :: payoutAddressJs :: changeAddressJs :: peerAddressJs :: Nil => + parseParameters(contractInfoJs, + collateralJs, + feeRateOptJs, + locktimeJs, + refundLTJs, + payoutAddressJs, + changeAddressJs, + peerAddressJs) case other => Failure( new IllegalArgumentException( @@ -905,7 +943,8 @@ object DecodeAttestations extends ServerJsonModels { case class AcceptDLCOffer( offer: LnMessage[DLCOfferTLV], externalPayoutAddressOpt: Option[BitcoinAddress], - externalChangeAddressOpt: Option[BitcoinAddress]) + externalChangeAddressOpt: Option[BitcoinAddress], + peerAddress: Option[InetSocketAddress]) object AcceptDLCOffer extends ServerJsonModels { @@ -913,7 +952,8 @@ object AcceptDLCOffer extends ServerJsonModels { def parseParameters( offerJs: Value, payoutAddressJs: Value, - changeAddressJs: Value) = Try { + changeAddressJs: Value, + peerAddressJs: Value) = Try { val offer = LnMessageFactory(DLCOfferTLV).fromHex(offerJs.str) val payoutAddressJsOpt = nullToOpt(payoutAddressJs) val payoutAddressOpt = @@ -921,16 +961,23 @@ object AcceptDLCOffer extends ServerJsonModels { val changeAddressJsOpt = nullToOpt(changeAddressJs) val changeAddressOpt = changeAddressJsOpt.map(js => jsToBitcoinAddress(js)) - AcceptDLCOffer(offer, payoutAddressOpt, changeAddressOpt) + val peerAddressJsOpt = nullToOpt(peerAddressJs) + val peerAddress = peerAddressJsOpt.map(js => jsToInetSocketAddress(js)) + AcceptDLCOffer(offer, payoutAddressOpt, changeAddressOpt, peerAddress) } jsArr.arr.toList match { case offerJs :: Nil => - parseParameters(offerJs, Null, Null) + parseParameters(offerJs, Null, Null, Null) case offerJs :: payoutAddressJs :: Nil => - parseParameters(offerJs, payoutAddressJs, Null) + parseParameters(offerJs, payoutAddressJs, Null, Null) case offerJs :: payoutAddressJs :: changeAddressJs :: Nil => - parseParameters(offerJs, payoutAddressJs, changeAddressJs) + parseParameters(offerJs, payoutAddressJs, changeAddressJs, Null) + case offerJs :: payoutAddressJs :: changeAddressJs :: peerAddressJs :: Nil => + parseParameters(offerJs, + payoutAddressJs, + changeAddressJs, + peerAddressJs) case Nil => Failure(new IllegalArgumentException("Missing offer argument")) @@ -963,9 +1010,7 @@ object AcceptDLC extends ServerJsonModels { case Failure(_) => LnMessage(DLCOfferTLV.fromHex(offerJs.str)) } - val uri = new URI("tcp://" + addrJs.str) - val peerAddr = - InetSocketAddress.createUnresolved(uri.getHost, uri.getPort) + val peerAddr = jsToInetSocketAddress(addrJs) val payoutAddressJsOpt = nullToOpt(payoutAddressJs) val payoutAddressOpt = payoutAddressJsOpt.map(js => jsToBitcoinAddress(js)) @@ -1391,7 +1436,7 @@ case class OfferAdd( peer: Option[String], message: Option[String]) -object OfferAdd { +object OfferAdd extends ServerJsonModels { def fromJsArr(arr: ujson.Arr): Try[OfferAdd] = { arr.arr.toList match { @@ -1435,14 +1480,14 @@ case class OfferSend( message: String, offerE: Either[DLCOfferTLV, Sha256Digest]) -object OfferSend { +object OfferSend extends ServerJsonModels { def fromJsArr(arr: ujson.Arr): Try[OfferSend] = { arr.arr.toList match { case offerJs :: peerAddressJs :: messageJs :: Nil => Try { val peerAddress = - NetworkUtil.parseInetSocketAddress(peerAddressJs.str, 2862) + jsToInetSocketAddress(peerAddressJs, DLC.DefaultPort) val message = messageJs.str val offerE = Try(LnMessageFactory(DLCOfferTLV).fromHex(offerJs.str).tlv) @@ -1598,4 +1643,17 @@ trait ServerJsonModels { js.arr.foldLeft(Vector.empty[OracleAttestmentTLV])((vec, tlv) => vec :+ jsToOracleAttestmentTLV(tlv)) } + + def jsToInetSocketAddress( + js: Value, + defaultPort: Int = -1): InetSocketAddress = { + js match { + case str: Str => + val uri = new URI("tcp://" + str.str) + val port = if (uri.getPort >= 0) uri.getPort else defaultPort + InetSocketAddress.createUnresolved(uri.getHost, port) + case _: Value => + throw Value.InvalidData(js, "Expected a host address") + } + } } diff --git a/app/server/src/main/scala/org/bitcoins/server/WalletRoutes.scala b/app/server/src/main/scala/org/bitcoins/server/WalletRoutes.scala index 5e02b229c9..200acc5564 100644 --- a/app/server/src/main/scala/org/bitcoins/server/WalletRoutes.scala +++ b/app/server/src/main/scala/org/bitcoins/server/WalletRoutes.scala @@ -300,11 +300,22 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit } } - case ServerCommand("getdlcs", _) => - complete { - wallet.listDLCs().map { dlcs => - Server.httpSuccess(dlcs.map(writeJs(_))) - } + case ServerCommand("getdlcs", arr) => + GetDLCs.fromJsArr(arr) match { + case Success(GetDLCs(Some(contactId))) => + complete { + wallet.listDLCsByContact(contactId).map { dlcs => + Server.httpSuccess(dlcs.map(writeJs(_))) + } + } + case Success(GetDLCs(None)) => + complete { + wallet.listDLCs().map { dlcs => + Server.httpSuccess(dlcs.map(writeJs(_))) + } + } + case Failure(exception) => + complete(Server.httpBadRequest(exception)) } case ServerCommand("getdlc", arr) => @@ -344,7 +355,8 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit locktimeOpt, refundLT, payoutAddressOpt, - changeAddressOpt)) => + changeAddressOpt, + peerAddressOpt)) => complete { val announcements = contractInfo.oracleInfo match { case OracleInfoV0TLV(announcement) => Vector(announcement) @@ -365,6 +377,7 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit feeRateOpt, locktime, refundLT, + peerAddressOpt, payoutAddressOpt, changeAddressOpt) case None => @@ -373,6 +386,7 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit collateral, feeRateOpt, refundLT, + peerAddressOpt, payoutAddressOpt, changeAddressOpt) } @@ -388,10 +402,16 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit case Failure(exception) => complete(Server.httpBadRequest(exception)) case Success( - AcceptDLCOffer(offer, payoutAddressOpt, changeAddressOpt)) => + AcceptDLCOffer(offer, + payoutAddressOpt, + changeAddressOpt, + peerAddressOpt)) => complete { wallet - .acceptDLCOffer(offer.tlv, payoutAddressOpt, changeAddressOpt) + .acceptDLCOffer(offer.tlv, + peerAddressOpt, + payoutAddressOpt, + changeAddressOpt) .map { accept => Server.httpSuccess(accept.toMessage.hex) } @@ -415,6 +435,7 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit wallet .acceptDLCOffer(offerMessage.tlv, + None, payoutAddressOpt, changeAddressOpt) .map { accept => diff --git a/core/src/main/scala/org/bitcoins/core/api/dlc/wallet/DLCWalletApi.scala b/core/src/main/scala/org/bitcoins/core/api/dlc/wallet/DLCWalletApi.scala index 07c784ea5a..d5cca88c31 100644 --- a/core/src/main/scala/org/bitcoins/core/api/dlc/wallet/DLCWalletApi.scala +++ b/core/src/main/scala/org/bitcoins/core/api/dlc/wallet/DLCWalletApi.scala @@ -28,6 +28,7 @@ trait DLCWalletApi { self: WalletApi => collateral: Satoshis, feeRateOpt: Option[SatoshisPerVirtualByte], refundLT: UInt32, + peerAddressOpt: Option[java.net.InetSocketAddress], externalPayoutAddressOpt: Option[BitcoinAddress], externalChangeAddressOpt: Option[BitcoinAddress]): Future[DLCOffer] = { val contractInfo = ContractInfo.fromTLV(contractInfoTLV) @@ -35,6 +36,7 @@ trait DLCWalletApi { self: WalletApi => collateral, feeRateOpt, refundLT, + peerAddressOpt, externalPayoutAddressOpt, externalChangeAddressOpt) } @@ -45,6 +47,7 @@ trait DLCWalletApi { self: WalletApi => feeRateOpt: Option[SatoshisPerVirtualByte], locktime: UInt32, refundLT: UInt32, + peerAddressOpt: Option[java.net.InetSocketAddress], externalPayoutAddressOpt: Option[BitcoinAddress], externalChangeAddressOpt: Option[BitcoinAddress]): Future[DLCOffer] = { val contractInfo = ContractInfo.fromTLV(contractInfoTLV) @@ -53,6 +56,7 @@ trait DLCWalletApi { self: WalletApi => feeRateOpt, locktime, refundLT, + peerAddressOpt, externalPayoutAddressOpt, externalChangeAddressOpt) } @@ -62,6 +66,7 @@ trait DLCWalletApi { self: WalletApi => collateral: Satoshis, feeRateOpt: Option[SatoshisPerVirtualByte], refundLT: UInt32, + peerAddressOpt: Option[java.net.InetSocketAddress], externalPayoutAddressOpt: Option[BitcoinAddress], externalChangeAddressOpt: Option[BitcoinAddress]): Future[DLCOffer] @@ -71,32 +76,24 @@ trait DLCWalletApi { self: WalletApi => feeRateOpt: Option[SatoshisPerVirtualByte], locktime: UInt32, refundLT: UInt32, + peerAddressOpt: Option[java.net.InetSocketAddress], externalPayoutAddressOpt: Option[BitcoinAddress], externalChangeAddressOpt: Option[BitcoinAddress]): Future[DLCOffer] - def registerDLCOffer(dlcOffer: DLCOffer): Future[DLCOffer] = { - createDLCOffer( - dlcOffer.contractInfo, - dlcOffer.collateral, - Some(dlcOffer.feeRate), - dlcOffer.timeouts.contractMaturity.toUInt32, - dlcOffer.timeouts.contractTimeout.toUInt32, - None, - None - ) - } - def acceptDLCOffer( dlcOfferTLV: DLCOfferTLV, + peerAddress: Option[InetSocketAddress], externalPayoutAddressOpt: Option[BitcoinAddress], externalChangeAddressOpt: Option[BitcoinAddress]): Future[DLCAccept] = { acceptDLCOffer(DLCOffer.fromTLV(dlcOfferTLV), + peerAddress, externalPayoutAddressOpt, externalChangeAddressOpt) } def acceptDLCOffer( dlcOffer: DLCOffer, + peerAddress: Option[InetSocketAddress], externalPayoutAddressOpt: Option[BitcoinAddress], externalChangeAddressOpt: Option[BitcoinAddress]): Future[DLCAccept] @@ -170,6 +167,14 @@ trait DLCWalletApi { self: WalletApi => def removeDLCContact(address: InetSocketAddress): Future[Unit] def findDLCContacts(alias: String): Future[Vector[DLCContactDb]] + + def addDLCContactMapping( + dlcId: Sha256Digest, + contactId: InetSocketAddress): Future[Unit] + + def removeDLCContactMapping(dlcId: Sha256Digest): Future[Unit] + + def listDLCsByContact(address: InetSocketAddress): Future[Vector[DLCStatus]] } /** An HDWallet that supports DLCs and Neutrino method of syncing */ diff --git a/core/src/main/scala/org/bitcoins/core/api/dlc/wallet/db/DLCContactDb.scala b/core/src/main/scala/org/bitcoins/core/api/dlc/wallet/db/DLCContactDb.scala index a753c932b2..8d177c2391 100644 --- a/core/src/main/scala/org/bitcoins/core/api/dlc/wallet/db/DLCContactDb.scala +++ b/core/src/main/scala/org/bitcoins/core/api/dlc/wallet/db/DLCContactDb.scala @@ -1,5 +1,20 @@ package org.bitcoins.core.api.dlc.wallet.db +import org.bitcoins.core.config.DLC +import org.bitcoins.core.util.NetworkUtil + import java.net.InetSocketAddress case class DLCContactDb(alias: String, address: InetSocketAddress, memo: String) + +object DLCContactDbHelper { + + def fromPeerAddress(peerAddress: String): DLCContactDb = + DLCContactDb( + alias = "", + address = + NetworkUtil.parseInetSocketAddress(peerAddress, DLC.DefaultPort), + memo = "" + ) + +} diff --git a/core/src/main/scala/org/bitcoins/core/api/dlc/wallet/db/DLCDb.scala b/core/src/main/scala/org/bitcoins/core/api/dlc/wallet/db/DLCDb.scala index 55ee1c2f29..898271d202 100644 --- a/core/src/main/scala/org/bitcoins/core/api/dlc/wallet/db/DLCDb.scala +++ b/core/src/main/scala/org/bitcoins/core/api/dlc/wallet/db/DLCDb.scala @@ -37,7 +37,8 @@ case class DLCDb( fundingTxIdOpt: Option[DoubleSha256DigestBE], closingTxIdOpt: Option[DoubleSha256DigestBE], aggregateSignatureOpt: Option[SchnorrDigitalSignature], - serializationVersion: DLCSerializationVersion + serializationVersion: DLCSerializationVersion, + peerOpt: Option[String] ) extends LastUpdatedDb { def updateState(newState: DLCState): DLCDb = { @@ -61,4 +62,8 @@ case class DLCDb( def updateAggregateSignature(sig: SchnorrDigitalSignature): DLCDb = { copy(aggregateSignatureOpt = Some(sig), lastUpdated = TimeUtil.now) } + + def updatePeer(peer: String): DLCDb = { + copy(peerOpt = Some(peer)) + } } diff --git a/core/src/main/scala/org/bitcoins/core/config/DLC.scala b/core/src/main/scala/org/bitcoins/core/config/DLC.scala new file mode 100644 index 0000000000..b6528210f6 --- /dev/null +++ b/core/src/main/scala/org/bitcoins/core/config/DLC.scala @@ -0,0 +1,7 @@ +package org.bitcoins.core.config + +object DLC { + + val DefaultPort: Int = 2862 + +} diff --git a/core/src/main/scala/org/bitcoins/core/protocol/dlc/models/DLCStatus.scala b/core/src/main/scala/org/bitcoins/core/protocol/dlc/models/DLCStatus.scala index 80dd156163..e42c60081f 100644 --- a/core/src/main/scala/org/bitcoins/core/protocol/dlc/models/DLCStatus.scala +++ b/core/src/main/scala/org/bitcoins/core/protocol/dlc/models/DLCStatus.scala @@ -35,6 +35,7 @@ sealed trait DLCStatus { def localCollateral: CurrencyUnit def remoteCollateral: CurrencyUnit = totalCollateral - localCollateral def payoutAddress: Option[PayoutAddress] + def peer(): Option[String] lazy val announcements: Vector[OracleAnnouncementTLV] = { oracleInfos.flatMap(_.singleOracleInfos.map(_.announcement)) @@ -92,7 +93,8 @@ object DLCStatus { feeRate: FeeUnit, totalCollateral: CurrencyUnit, localCollateral: CurrencyUnit, - payoutAddress: Option[PayoutAddress] + payoutAddress: Option[PayoutAddress], + peer: Option[String] ) extends DLCStatus { override val state: DLCState.Offered.type = DLCState.Offered } @@ -108,7 +110,8 @@ object DLCStatus { feeRate: FeeUnit, totalCollateral: CurrencyUnit, localCollateral: CurrencyUnit, - payoutAddress: Option[PayoutAddress]) + payoutAddress: Option[PayoutAddress], + peer: Option[String]) extends AcceptedDLCStatus { override val state: DLCState.AcceptComputingAdaptorSigs.type = @@ -126,7 +129,8 @@ object DLCStatus { feeRate: FeeUnit, totalCollateral: CurrencyUnit, localCollateral: CurrencyUnit, - payoutAddress: Option[PayoutAddress]) + payoutAddress: Option[PayoutAddress], + peer: Option[String]) extends AcceptedDLCStatus { override val state: DLCState.Accepted.type = DLCState.Accepted } @@ -143,7 +147,8 @@ object DLCStatus { totalCollateral: CurrencyUnit, localCollateral: CurrencyUnit, fundingTxId: DoubleSha256DigestBE, - payoutAddress: Option[PayoutAddress]) + payoutAddress: Option[PayoutAddress], + peer: Option[String]) extends SignedDLCStatus { override val state: DLCState.SignComputingAdaptorSigs.type = @@ -162,7 +167,8 @@ object DLCStatus { totalCollateral: CurrencyUnit, localCollateral: CurrencyUnit, fundingTxId: DoubleSha256DigestBE, - payoutAddress: Option[PayoutAddress]) + payoutAddress: Option[PayoutAddress], + peer: Option[String]) extends SignedDLCStatus { override val state: DLCState.Signed.type = DLCState.Signed } @@ -179,7 +185,8 @@ object DLCStatus { totalCollateral: CurrencyUnit, localCollateral: CurrencyUnit, fundingTxId: DoubleSha256DigestBE, - payoutAddress: Option[PayoutAddress]) + payoutAddress: Option[PayoutAddress], + peer: Option[String]) extends SignedDLCStatus { override val state: DLCState.Broadcasted.type = DLCState.Broadcasted } @@ -196,7 +203,8 @@ object DLCStatus { totalCollateral: CurrencyUnit, localCollateral: CurrencyUnit, fundingTxId: DoubleSha256DigestBE, - payoutAddress: Option[PayoutAddress]) + payoutAddress: Option[PayoutAddress], + peer: Option[String]) extends SignedDLCStatus { override val state: DLCState.Confirmed.type = DLCState.Confirmed } @@ -218,7 +226,8 @@ object DLCStatus { oracleOutcome: OracleOutcome, myPayout: CurrencyUnit, counterPartyPayout: CurrencyUnit, - payoutAddress: Option[PayoutAddress]) + payoutAddress: Option[PayoutAddress], + peer: Option[String]) extends ClaimedDLCStatus { override val state: DLCState.Claimed.type = DLCState.Claimed } @@ -240,7 +249,8 @@ object DLCStatus { oracleOutcome: OracleOutcome, myPayout: CurrencyUnit, counterPartyPayout: CurrencyUnit, - payoutAddress: Option[PayoutAddress]) + payoutAddress: Option[PayoutAddress], + peer: Option[String]) extends ClaimedDLCStatus { override val state: DLCState.RemoteClaimed.type = DLCState.RemoteClaimed override val oracleSigs: Vector[SchnorrDigitalSignature] = Vector(oracleSig) @@ -261,7 +271,8 @@ object DLCStatus { closingTxId: DoubleSha256DigestBE, myPayout: CurrencyUnit, counterPartyPayout: CurrencyUnit, - payoutAddress: Option[PayoutAddress]) + payoutAddress: Option[PayoutAddress], + peer: Option[String]) extends ClosedDLCStatus { override val state: DLCState.Refunded.type = DLCState.Refunded } diff --git a/db-commons-test/src/test/scala/org/bitcoins/db/DbManagementTest.scala b/db-commons-test/src/test/scala/org/bitcoins/db/DbManagementTest.scala index 2f621d85d5..e15a458223 100644 --- a/db-commons-test/src/test/scala/org/bitcoins/db/DbManagementTest.scala +++ b/db-commons-test/src/test/scala/org/bitcoins/db/DbManagementTest.scala @@ -83,13 +83,13 @@ class DbManagementTest extends BitcoinSAsyncTest with EmbeddedPg { val result = dlcDbManagement.migrate() dlcAppConfig.driver match { case SQLite => - val expected = 7 + val expected = 8 assert(result.migrationsExecuted == expected) val flywayInfo = dlcAppConfig.info() assert(flywayInfo.applied().length == expected) assert(flywayInfo.pending().length == 0) case PostgreSQL => - val expected = 8 + val expected = 9 assert(result.migrationsExecuted == expected) val flywayInfo = dlcAppConfig.info() diff --git a/dlc-node-test/src/test/scala/org/bitcoins/dlc/node/DLCNegotiationTest.scala b/dlc-node-test/src/test/scala/org/bitcoins/dlc/node/DLCNegotiationTest.scala index c92d00eee7..29c162257c 100644 --- a/dlc-node-test/src/test/scala/org/bitcoins/dlc/node/DLCNegotiationTest.scala +++ b/dlc-node-test/src/test/scala/org/bitcoins/dlc/node/DLCNegotiationTest.scala @@ -56,8 +56,9 @@ class DLCNegotiationTest extends BitcoinSDualWalletTest { UInt32.zero, UInt32.one, None, + None, None) - accept <- walletA.acceptDLCOffer(offer, None, None) + accept <- walletA.acceptDLCOffer(offer, None, None, None) // Send accept message to begin p2p _ = handler ! DLCDataHandler.Received(accept.toMessage) @@ -108,6 +109,7 @@ class DLCNegotiationTest extends BitcoinSDualWalletTest { UInt32.zero, UInt32.one, None, + None, None) tlv = SendOfferTLV(peer = "peer", message = "msg", offer = offer.toTLV) diff --git a/dlc-node-test/src/test/scala/org/bitcoins/dlc/node/DLCNodeTest.scala b/dlc-node-test/src/test/scala/org/bitcoins/dlc/node/DLCNodeTest.scala index 7878e49553..871bde9e73 100644 --- a/dlc-node-test/src/test/scala/org/bitcoins/dlc/node/DLCNodeTest.scala +++ b/dlc-node-test/src/test/scala/org/bitcoins/dlc/node/DLCNodeTest.scala @@ -37,6 +37,7 @@ class DLCNodeTest extends BitcoinSDLCNodeTest { UInt32.zero, UInt32.one, None, + None, None) _ <- nodeB.acceptDLCOffer(addrA, offer.toMessage, None, None) diff --git a/dlc-node/src/main/scala/org/bitcoins/dlc/node/DLCNode.scala b/dlc-node/src/main/scala/org/bitcoins/dlc/node/DLCNode.scala index 5d6b08f053..445c7f6bad 100644 --- a/dlc-node/src/main/scala/org/bitcoins/dlc/node/DLCNode.scala +++ b/dlc-node/src/main/scala/org/bitcoins/dlc/node/DLCNode.scala @@ -66,6 +66,7 @@ case class DLCNode(wallet: DLCWalletApi)(implicit for { handler <- connectToPeer(peerAddress) accept <- wallet.acceptDLCOffer(dlcOffer.tlv, + Some(peerAddress), externalPayoutAddress, externalChangeAddress) } yield { diff --git a/dlc-wallet-test/src/test/scala/org/bitcoins/dlc/wallet/DLCDAOTest.scala b/dlc-wallet-test/src/test/scala/org/bitcoins/dlc/wallet/DLCDAOTest.scala index c0eb4ec359..68aa73697f 100644 --- a/dlc-wallet-test/src/test/scala/org/bitcoins/dlc/wallet/DLCDAOTest.scala +++ b/dlc-wallet-test/src/test/scala/org/bitcoins/dlc/wallet/DLCDAOTest.scala @@ -1,6 +1,6 @@ package org.bitcoins.dlc.wallet -import org.bitcoins.core.api.dlc.wallet.db.DLCDb +import org.bitcoins.core.api.dlc.wallet.db.{DLCContactDb, DLCDb} import org.bitcoins.core.api.wallet.db.TransactionDbHelper import org.bitcoins.core.currency.Satoshis import org.bitcoins.core.number.{UInt32, UInt64} @@ -17,6 +17,8 @@ import org.bitcoins.testkit.fixtures.DLCDAOFixture import org.bitcoins.testkit.wallet.{BitcoinSWalletTest, DLCWalletUtil} import org.scalatest.Assertion +import java.net.InetSocketAddress +import java.sql.SQLException import scala.concurrent.Future class DLCDAOTest extends BitcoinSWalletTest with DLCDAOFixture { @@ -219,4 +221,39 @@ class DLCDAOTest extends BitcoinSWalletTest with DLCDAOFixture { verifyDatabaseInsertion(tx, tx.txIdBE, remoteTxDAO, daos.dlcDAO) } + + it should "update peer" in { daos => + val contact = DLCContactDb( + address = InetSocketAddress.createUnresolved("127.0.0.1", 1), + alias = "alias", + memo = "memo" + ) + for { + // no contact + _ <- recoverToSucceededIf[SQLException]( + daos.dlcDAO.updateDLCContactMapping(dlcId, contact.address)) + + _ <- daos.contactDAO.create(contact) + + // no dlc + _ <- recoverToSucceededIf[SQLException]( + daos.dlcDAO.updateDLCContactMapping(dlcId, contact.address)) + _ <- recoverToSucceededIf[SQLException]( + daos.dlcDAO.deleteDLCContactMapping(dlcId)) + + created <- daos.dlcDAO.create(dlcDb) + + _ <- daos.dlcDAO.updateDLCContactMapping(dlcId, contact.address) + + updated <- daos.dlcDAO.read(dlcId) + + _ <- daos.dlcDAO.deleteDLCContactMapping(dlcId) + + deleted <- daos.dlcDAO.read(dlcId) + } yield { + assert(created.peerOpt.isEmpty) + assert(updated.get.peerOpt == Some("127.0.0.1:1")) + assert(deleted.get.peerOpt.isEmpty) + } + } } diff --git a/dlc-wallet-test/src/test/scala/org/bitcoins/dlc/wallet/DLCExecutionTest.scala b/dlc-wallet-test/src/test/scala/org/bitcoins/dlc/wallet/DLCExecutionTest.scala index 84044acaa9..f46f76c4f4 100644 --- a/dlc-wallet-test/src/test/scala/org/bitcoins/dlc/wallet/DLCExecutionTest.scala +++ b/dlc-wallet-test/src/test/scala/org/bitcoins/dlc/wallet/DLCExecutionTest.scala @@ -371,6 +371,7 @@ class DLCExecutionTest extends BitcoinSDualWalletTest { feeRateOpt = feeRateOpt, locktime = UInt32.zero, refundLT = UInt32.one, + peerAddressOpt = None, externalPayoutAddressOpt = None, externalChangeAddressOpt = None ) @@ -424,6 +425,7 @@ class DLCExecutionTest extends BitcoinSDualWalletTest { UInt32.zero, UInt32.one, None, + None, None) _ <- walletA.listDLCs() @@ -481,10 +483,11 @@ class DLCExecutionTest extends BitcoinSDualWalletTest { feeRateOpt = Some(SatoshisPerVirtualByte.fromLong(10)), locktime = dummyTimeouts.contractMaturity.toUInt32, refundLocktime = dummyTimeouts.contractTimeout.toUInt32, + peerAddressOpt = None, externalPayoutAddressOpt = None, externalChangeAddressOpt = None ) - accept <- walletB.acceptDLCOffer(offer, None, None) + accept <- walletB.acceptDLCOffer(offer, None, None, None) sign <- walletA.signDLC(accept) contractId = sign.contractId (_, sig) = DLCWalletUtil.getSigs(contractInfo) diff --git a/dlc-wallet-test/src/test/scala/org/bitcoins/dlc/wallet/MultiWalletDLCTest.scala b/dlc-wallet-test/src/test/scala/org/bitcoins/dlc/wallet/MultiWalletDLCTest.scala index 78d57c62df..f371e9339e 100644 --- a/dlc-wallet-test/src/test/scala/org/bitcoins/dlc/wallet/MultiWalletDLCTest.scala +++ b/dlc-wallet-test/src/test/scala/org/bitcoins/dlc/wallet/MultiWalletDLCTest.scala @@ -53,6 +53,7 @@ class MultiWalletDLCTest extends BitcoinSWalletTest { UInt32.zero, UInt32.one, None, + None, None) dlcsA <- walletA.listDLCs() dlcsB <- walletB.listDLCs() @@ -76,6 +77,7 @@ class MultiWalletDLCTest extends BitcoinSWalletTest { feeRateOpt = Some(SatoshisPerVirtualByte.one), locktime = UInt32.zero, refundLocktime = UInt32.one, + peerAddressOpt = None, externalPayoutAddressOpt = None, externalChangeAddressOpt = None ) diff --git a/dlc-wallet-test/src/test/scala/org/bitcoins/dlc/wallet/WalletDLCSetupTest.scala b/dlc-wallet-test/src/test/scala/org/bitcoins/dlc/wallet/WalletDLCSetupTest.scala index 530f3100da..0005f15541 100644 --- a/dlc-wallet-test/src/test/scala/org/bitcoins/dlc/wallet/WalletDLCSetupTest.scala +++ b/dlc-wallet-test/src/test/scala/org/bitcoins/dlc/wallet/WalletDLCSetupTest.scala @@ -20,6 +20,7 @@ import org.bitcoins.testkit.wallet.FundWalletUtil.FundedDLCWallet import org.bitcoins.testkit.wallet.{BitcoinSDualWalletTest, DLCWalletUtil} import org.scalatest.{Assertion, FutureOutcome} +import java.net.InetSocketAddress import scala.concurrent.Future import scala.reflect.ClassTag @@ -47,6 +48,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { offerData.timeouts.contractMaturity.toUInt32, offerData.timeouts.contractTimeout.toUInt32, None, + None, None ) dlcId = calcDLCId(offer.fundingInputs.map(_.outPoint)) @@ -64,7 +66,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { assert(offer.changeAddress.value.nonEmpty) } - accept <- walletB.acceptDLCOffer(offer, None, None) + accept <- walletB.acceptDLCOffer(offer, None, None, None) dlcB1Opt <- walletB.dlcDAO.read(dlcId) _ = { assert(dlcB1Opt.isDefined) @@ -189,11 +191,12 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { offerData.timeouts.contractMaturity.toUInt32, offerData.timeouts.contractTimeout.toUInt32, None, + None, None ) dlcId = calcDLCId(offer.fundingInputs.map(_.outPoint)) - accept <- walletB.acceptDLCOffer(offer, None, None) + accept <- walletB.acceptDLCOffer(offer, None, None, None) // reorder dlc inputs in wallets _ <- reorderInputDbs(walletA, dlcId) @@ -248,11 +251,12 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { offerData.timeouts.contractMaturity.toUInt32, offerData.timeouts.contractTimeout.toUInt32, None, + None, None ) dlcId = calcDLCId(offer.fundingInputs.map(_.outPoint)) - accept <- walletB.acceptDLCOffer(offer.toTLV, None, None) + accept <- walletB.acceptDLCOffer(offer.toTLV, None, None, None) // reorder dlc inputs in wallets _ <- reorderInputDbs(walletA, dlcId) @@ -279,6 +283,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { offerData.timeouts.contractMaturity.toUInt32, offerData.timeouts.contractTimeout.toUInt32, None, + None, None ) dlcId = calcDLCId(offer.fundingInputs.map(_.outPoint)) @@ -295,7 +300,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { assert(offer.changeAddress.value.nonEmpty) } - accept <- walletB.acceptDLCOffer(offer.toTLV, None, None) + accept <- walletB.acceptDLCOffer(offer.toTLV, None, None, None) dlcB1Opt <- walletB.dlcDAO.read(dlcId) _ = { assert(dlcB1Opt.isDefined) @@ -374,10 +379,11 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { offerData.timeouts.contractMaturity.toUInt32, offerData.timeouts.contractTimeout.toUInt32, None, + None, None ) - accept <- walletB.acceptDLCOffer(offer, None, None) + accept <- walletB.acceptDLCOffer(offer, None, None, None) } yield accept } @@ -520,6 +526,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { offerData.timeouts.contractMaturity.toUInt32, offerData.timeouts.contractTimeout.toUInt32, None, + None, None ) @@ -565,9 +572,10 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { offerData.timeouts.contractMaturity.toUInt32, offerData.timeouts.contractTimeout.toUInt32, None, + None, None ) - _ <- walletB.acceptDLCOffer(offer, None, None) + _ <- walletB.acceptDLCOffer(offer, None, None, None) dlcId = calcDLCId(offer.fundingInputs.map(_.outPoint)) @@ -608,9 +616,10 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { offerData.timeouts.contractMaturity.toUInt32, offerData.timeouts.contractTimeout.toUInt32, None, + None, None ) - accept <- walletB.acceptDLCOffer(offer, None, None) + accept <- walletB.acceptDLCOffer(offer, None, None, None) sign <- walletA.signDLC(accept) _ <- walletB.addDLCSigs(sign) @@ -652,9 +661,10 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { offerData.timeouts.contractMaturity.toUInt32, offerData.timeouts.contractTimeout.toUInt32, None, + None, None ) - accept <- walletB.acceptDLCOffer(offer, None, None) + accept <- walletB.acceptDLCOffer(offer, None, None, None) sign <- walletA.signDLC(accept) _ <- walletB.addDLCSigs(sign) @@ -687,9 +697,10 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { offerData.timeouts.contractMaturity.toUInt32, UInt32.max, None, + None, None ) - accept <- walletB.acceptDLCOffer(offer, None, None) + accept <- walletB.acceptDLCOffer(offer, None, None, None) sign <- walletA.signDLC(accept) _ <- walletB.addDLCSigs(sign) @@ -754,6 +765,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { offerData.timeouts.contractMaturity.toUInt32, offerData.timeouts.contractTimeout.toUInt32, None, + None, None ) _ = { @@ -768,7 +780,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { dlcId = calcDLCId(offer.fundingInputs.map(_.outPoint)) - accept <- walletB.acceptDLCOffer(offer, None, None) + accept <- walletB.acceptDLCOffer(offer, None, None, None) _ = { assert(accept.fundingInputs.nonEmpty) assert(accept.collateral == offer.contractInfo.max - offer.collateral) @@ -851,6 +863,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { feeRateOpt = feeRateOpt, locktime = UInt32.zero, refundLT = UInt32.one, + peerAddressOpt = None, externalPayoutAddressOpt = None, externalChangeAddressOpt = None ) @@ -860,8 +873,8 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { offerA <- makeOffer(contractInfoA) offerB <- makeOffer(contractInfoB) - _ <- walletB.acceptDLCOffer(offerA, None, None) - _ <- walletB.acceptDLCOffer(offerB, None, None) + _ <- walletB.acceptDLCOffer(offerA, None, None, None) + _ <- walletB.acceptDLCOffer(offerB, None, None, None) } yield succeed } @@ -899,6 +912,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { feeRateOpt = feeRateOpt, locktime = UInt32.zero, refundLT = UInt32.one, + peerAddressOpt = None, externalPayoutAddressOpt = None, externalChangeAddressOpt = None ) @@ -906,8 +920,8 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { for { offer <- makeOffer(contractInfoA) - accept1F = walletB.acceptDLCOffer(offer, None, None) - accept2F = walletB.acceptDLCOffer(offer, None, None) + accept1F = walletB.acceptDLCOffer(offer, None, None, None) + accept2F = walletB.acceptDLCOffer(offer, None, None, None) _ <- recoverToSucceededIf[DuplicateOfferException]( Future.sequence(Seq(accept1F, accept2F))) } yield { @@ -930,6 +944,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { feeRateOpt = feeRateOpt, locktime = UInt32.zero, refundLT = UInt32.one, + peerAddressOpt = None, externalPayoutAddressOpt = None, externalChangeAddressOpt = None ) @@ -937,8 +952,8 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { for { offer <- makeOffer(contractInfoA) - accept1 <- walletB.acceptDLCOffer(offer, None, None) - accept2 <- walletB.acceptDLCOffer(offer, None, None) + accept1 <- walletB.acceptDLCOffer(offer, None, None, None) + accept2 <- walletB.acceptDLCOffer(offer, None, None, None) } yield { assert(accept1 == accept2) } @@ -958,9 +973,10 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { offerData.timeouts.contractMaturity.toUInt32, UInt32.max, None, + None, None ) - accept <- walletB.acceptDLCOffer(offer, None, None) + accept <- walletB.acceptDLCOffer(offer, None, None, None) res <- recoverToSucceededIf[IllegalArgumentException]( walletB.signDLC(accept)) } yield res @@ -981,6 +997,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { offerData.timeouts.contractMaturity.toUInt32, UInt32.max, None, + None, None )) } yield { @@ -1006,12 +1023,13 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { feeRateOpt = feeRateOpt, locktime = UInt32.zero, refundLT = UInt32.one, + peerAddressOpt = None, externalPayoutAddressOpt = None, externalChangeAddressOpt = None ) invalidOffer = offer.copy(contractInfo = invalidContractInfo) res <- recoverToSucceededIf[InvalidAnnouncementSignature]( - walletB.acceptDLCOffer(invalidOffer, None, None)) + walletB.acceptDLCOffer(invalidOffer, None, None, None)) } yield { res } @@ -1042,6 +1060,12 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { Some(BitcoinAddress.fromString("2MsM67NLa71fHvTUBqNENW15P68nHB2vVXb")) val changeAddressBOpt = Some(BitcoinAddress.fromString("2N4YXTxKEso3yeYXNn5h42Vqu3FzTTQ8Lq5")) + val peerAddressOpt1 = + Some(InetSocketAddress.createUnresolved("127.0.0.1", 1)) + val peerAddressOpt2 = + Some(InetSocketAddress.createUnresolved("127.0.0.1", 2)) + val peerAddressOpt3 = + Some(InetSocketAddress.createUnresolved("127.0.0.1", 3)) for { offer <- walletA.createDLCOffer( @@ -1050,10 +1074,12 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { feeRateOpt = feeRateOpt, locktime = UInt32.zero, refundLT = UInt32.one, + peerAddressOpt = peerAddressOpt1, externalPayoutAddressOpt = payoutAddressAOpt, externalChangeAddressOpt = changeAddressAOpt ) accept <- walletB.acceptDLCOffer(offer, + peerAddressOpt2, payoutAddressBOpt, changeAddressBOpt) offer1 <- walletA.createDLCOffer( @@ -1062,10 +1088,11 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { feeRateOpt = feeRateOpt1, locktime = UInt32.zero, refundLT = UInt32.one, + peerAddressOpt = peerAddressOpt3, externalPayoutAddressOpt = None, externalChangeAddressOpt = None ) - accept1 <- walletB.acceptDLCOffer(offer1, None, None) + accept1 <- walletB.acceptDLCOffer(offer1, peerAddressOpt3, None, None) } yield { assert(offer.pubKeys.payoutAddress == payoutAddressAOpt.get) assert(offer.changeAddress == changeAddressAOpt.get) @@ -1101,10 +1128,11 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { offerData.timeouts.contractMaturity.toUInt32, offerData.timeouts.contractTimeout.toUInt32, None, + None, None ) //accept it for the first time using the inputs - _ <- walletB.acceptDLCOffer(offer1.toTLV, None, None) + _ <- walletB.acceptDLCOffer(offer1.toTLV, None, None, None) //cancel the offer _ <- walletA.cancelDLC(dlcId = offer1.dlcId) @@ -1115,9 +1143,10 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { offerData2.timeouts.contractMaturity.toUInt32, offerData2.timeouts.contractTimeout.toUInt32, None, + None, None ) - _ <- walletB.acceptDLCOffer(offer2.toTLV, None, None) + _ <- walletB.acceptDLCOffer(offer2.toTLV, None, None, None) } yield succeed } } diff --git a/dlc-wallet-test/src/test/scala/org/bitcoins/dlc/wallet/internal/DLCDataManagementTest.scala b/dlc-wallet-test/src/test/scala/org/bitcoins/dlc/wallet/internal/DLCDataManagementTest.scala index 566768339a..a8bb7844a0 100644 --- a/dlc-wallet-test/src/test/scala/org/bitcoins/dlc/wallet/internal/DLCDataManagementTest.scala +++ b/dlc-wallet-test/src/test/scala/org/bitcoins/dlc/wallet/internal/DLCDataManagementTest.scala @@ -33,9 +33,10 @@ class DLCDataManagementTest extends BitcoinSDualWalletTest { offerData.timeouts.contractMaturity.toUInt32, offerData.timeouts.contractTimeout.toUInt32, None, + None, None ) - accept <- walletB.acceptDLCOffer(offer1, None, None) + accept <- walletB.acceptDLCOffer(offer1, None, None, None) contractId = DLCUtil.calcContractId(offer1, accept) acceptDbStateOpt <- walletB.dlcDataManagement.getDLCFundingData( contractId, @@ -62,9 +63,10 @@ class DLCDataManagementTest extends BitcoinSDualWalletTest { offerData.timeouts.contractMaturity.toUInt32, offerData.timeouts.contractTimeout.toUInt32, None, + None, None ) - accept <- walletB.acceptDLCOffer(offer1, None, None) + accept <- walletB.acceptDLCOffer(offer1, None, None, None) sign <- walletA.signDLC(accept) signDbStateOpt <- walletA.dlcDataManagement.getDLCFundingData( diff --git a/dlc-wallet/src/main/resources/postgresql/dlc/migration/V9__contact_global_dlc_data.sql b/dlc-wallet/src/main/resources/postgresql/dlc/migration/V9__contact_global_dlc_data.sql new file mode 100644 index 0000000000..3f6b409c11 --- /dev/null +++ b/dlc-wallet/src/main/resources/postgresql/dlc/migration/V9__contact_global_dlc_data.sql @@ -0,0 +1,3 @@ +ALTER TABLE "global_dlc_data" ADD COLUMN "peer" VARCHAR(1024); + +CREATE INDEX "global_dlc_data_peer_idx" ON "global_dlc_data"("peer"); diff --git a/dlc-wallet/src/main/resources/sqlite/dlc/migration/V8__contact_global_dlc_data.sql b/dlc-wallet/src/main/resources/sqlite/dlc/migration/V8__contact_global_dlc_data.sql new file mode 100644 index 0000000000..3f6b409c11 --- /dev/null +++ b/dlc-wallet/src/main/resources/sqlite/dlc/migration/V8__contact_global_dlc_data.sql @@ -0,0 +1,3 @@ +ALTER TABLE "global_dlc_data" ADD COLUMN "peer" VARCHAR(1024); + +CREATE INDEX "global_dlc_data_peer_idx" ON "global_dlc_data"("peer"); diff --git a/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/DLCWallet.scala b/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/DLCWallet.scala index 5619c8a288..bc84a0a174 100644 --- a/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/DLCWallet.scala +++ b/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/DLCWallet.scala @@ -43,6 +43,7 @@ import org.bitcoins.wallet.{Wallet, WalletLogger} import scodec.bits.ByteVector import slick.dbio.{DBIO, DBIOAction} +import java.net.InetSocketAddress import scala.concurrent.{ExecutionContext, Future} /** A [[Wallet]] with full DLC Functionality */ @@ -273,6 +274,7 @@ abstract class DLCWallet collateral: Satoshis, feeRateOpt: Option[SatoshisPerVirtualByte], refundLT: UInt32, + peerAddressOpt: Option[java.net.InetSocketAddress], externalPayoutAddressOpt: Option[BitcoinAddress], externalChangeAddressOpt: Option[BitcoinAddress]): Future[DLCOffer] = { chainQueryApi.getBestHashBlockHeight().flatMap { height => @@ -281,6 +283,7 @@ abstract class DLCWallet feeRateOpt, locktime = UInt32(height), refundLT, + peerAddressOpt, externalPayoutAddressOpt, externalChangeAddressOpt) } @@ -297,6 +300,7 @@ abstract class DLCWallet feeRateOpt: Option[SatoshisPerVirtualByte], locktime: UInt32, refundLocktime: UInt32, + peerAddressOpt: Option[java.net.InetSocketAddress], externalPayoutAddressOpt: Option[BitcoinAddress], externalChangeAddressOpt: Option[BitcoinAddress]): Future[DLCOffer] = { logger.info("Creating DLC Offer") @@ -434,7 +438,8 @@ abstract class DLCWallet fundingTxIdOpt = None, closingTxIdOpt = None, aggregateSignatureOpt = None, - serializationVersion = contractInfo.serializationVersion + serializationVersion = contractInfo.serializationVersion, + peerOpt = peerAddressOpt.map(a => a.getHostString + ":" + a.getPort) ) contractDataDb = DLCContractDataDb( @@ -485,6 +490,7 @@ abstract class DLCWallet txBuilder: RawTxBuilderWithFinalizer[ShufflingNonInteractiveFinalizer], spendingInfos: Vector[ScriptSignatureParams[InputInfo]], collateral: CurrencyUnit, + peerAddressOpt: Option[InetSocketAddress], externalPayoutAddressOpt: Option[BitcoinAddress], externalChangeAddressOpt: Option[BitcoinAddress] ): Future[InitializedAccept] = { @@ -553,13 +559,16 @@ abstract class DLCWallet (dlcAcceptWithoutSigs, dlcPubKeys) <- acceptWithoutSigsWithKeysF nextIndex <- nextIndexF contractId = DLCUtil.calcContractId(offer, dlcAcceptWithoutSigs) - dlc = DLCAcceptUtil.buildAcceptDlcDb(offer, - dlcId, - Some(contractId), - account, - chainType, - nextIndex, - contractInfo) + dlc = DLCAcceptUtil.buildAcceptDlcDb( + offer, + dlcId, + Some(contractId), + account, + chainType, + nextIndex, + contractInfo, + peerOpt = + peerAddressOpt.map(a => a.getHostString + ":" + a.getPort)) acceptDb = DLCAcceptUtil.buildAcceptDb(dlc = dlc, acceptWithoutSigs = dlcAcceptWithoutSigs, @@ -662,6 +671,7 @@ abstract class DLCWallet */ override def acceptDLCOffer( offer: DLCOffer, + peerAddressOpt: Option[java.net.InetSocketAddress], externalPayoutAddressOpt: Option[BitcoinAddress], externalChangeAddressOpt: Option[BitcoinAddress]): Future[DLCAccept] = { logger.debug("Calculating relevant wallet data for DLC Accept") @@ -693,6 +703,7 @@ abstract class DLCWallet case None => createNewDLCAccept(collateral, offer, + peerAddressOpt, externalPayoutAddressOpt, externalChangeAddressOpt) } @@ -744,6 +755,7 @@ abstract class DLCWallet private def createNewDLCAccept( collateral: CurrencyUnit, offer: DLCOffer, + peerAddressOpt: Option[java.net.InetSocketAddress], externalPayoutAddressOpt: Option[BitcoinAddress], externalChangeAddressOpt: Option[BitcoinAddress]): Future[DLCAccept] = Future { @@ -764,7 +776,8 @@ abstract class DLCWallet spendingInfos = spendingInfos, collateral = collateral, externalPayoutAddressOpt = externalPayoutAddressOpt, - externalChangeAddressOpt = externalChangeAddressOpt + externalChangeAddressOpt = externalChangeAddressOpt, + peerAddressOpt = peerAddressOpt ) _ = require( initializedAccept.acceptWithoutSigs.tempContractId == offer.tempContractId, @@ -1696,8 +1709,24 @@ abstract class DLCWallet } override def listDLCs(): Future[Vector[DLCStatus]] = { + listDLCs(None) + } + + override def listDLCsByContact( + contactId: InetSocketAddress): Future[Vector[DLCStatus]] = { + listDLCs(Some(contactId)) + } + + private def listDLCs( + contactIdOpt: Option[InetSocketAddress]): Future[Vector[DLCStatus]] = { for { - ids <- dlcDAO.findAll().map(_.map(_.dlcId)) + dlcs <- contactIdOpt match { + case Some(contactId) => + dlcDAO.findByContactId( + contactId.getHostString + ":" + contactId.getPort) + case None => dlcDAO.findAll() + } + ids = dlcs.map(_.dlcId) dlcFs = ids.map(findDLC) dlcs <- Future.sequence(dlcFs) } yield { diff --git a/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/internal/IncomingDLCOffersHandling.scala b/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/internal/IncomingDLCOffersHandling.scala index d7e3461dc2..be307328e9 100644 --- a/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/internal/IncomingDLCOffersHandling.scala +++ b/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/internal/IncomingDLCOffersHandling.scala @@ -1,6 +1,10 @@ package org.bitcoins.dlc.wallet.internal -import org.bitcoins.core.api.dlc.wallet.db.{DLCContactDb, IncomingDLCOfferDb} +import org.bitcoins.core.api.dlc.wallet.db.{ + DLCContactDb, + DLCContactDbHelper, + IncomingDLCOfferDb +} import org.bitcoins.core.protocol.tlv.DLCOfferTLV import org.bitcoins.crypto.Sha256Digest import org.bitcoins.dlc.wallet.DLCWallet @@ -13,13 +17,19 @@ trait IncomingDLCOffersHandling { self: DLCWallet => def registerIncomingDLCOffer( offerTLV: DLCOfferTLV, - peer: Option[String], + peerOpt: Option[String], message: Option[String]): Future[Sha256Digest] = { val dbo = IncomingDLCOfferDbHelper.fromTLV(offerTLV = offerTLV, - peer = peer, + peer = peerOpt, message = message) + val contactDbOpt = peerOpt.map(DLCContactDbHelper.fromPeerAddress) for { added <- dlcWalletDAOs.incomingDLCOfferDAO.create(dbo) + _ <- contactDbOpt match { + case Some(contactDb) => + dlcWalletDAOs.contactDAO.createIfDoesNotExist(contactDb) + case None => Future.successful(()) + } _ <- dlcConfig.walletCallbacks.executeOnDLCOfferAdd(logger, added) } yield dbo.hash } @@ -58,4 +68,14 @@ trait IncomingDLCOffersHandling { self: DLCWallet => override def findDLCContacts(alias: String): Future[Vector[DLCContactDb]] = contactDAO.findByAlias(alias) + override def addDLCContactMapping( + dlcId: Sha256Digest, + contcatId: InetSocketAddress): Future[Unit] = { + dlcDAO + .updateDLCContactMapping(dlcId, contcatId) + } + + override def removeDLCContactMapping(dlcId: Sha256Digest): Future[Unit] = { + dlcDAO.deleteDLCContactMapping(dlcId) + } } diff --git a/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/models/DLCContactDAO.scala b/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/models/DLCContactDAO.scala index 45743e64b7..2e5e80ad14 100644 --- a/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/models/DLCContactDAO.scala +++ b/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/models/DLCContactDAO.scala @@ -24,6 +24,23 @@ case class DLCContactDAO()(implicit ts: Vector[DLCContactDb]): Future[Vector[DLCContactDb]] = createAllNoAutoInc(ts, safeDatabase) + def createIfDoesNotExist(contact: DLCContactDb): Future[DLCContactDb] = { + val action = for { + foundOpt <- table + .filter(_.address === contact.address) + .result + .headOption + result <- + foundOpt match { + case Some(found) => DBIO.successful(found) + case None => + (table += contact).map(_ => contact) + } + } yield result + + safeDatabase.run(action) + } + override protected def findByPrimaryKeys(ids: Vector[ InetSocketAddress]): Query[DLCContactTable, DLCContactDb, Seq] = table.filter(_.address.inSet(ids)).sortBy(_.alias) diff --git a/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/models/DLCDAO.scala b/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/models/DLCDAO.scala index ad7b7ddb11..3e11f32f46 100644 --- a/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/models/DLCDAO.scala +++ b/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/models/DLCDAO.scala @@ -13,6 +13,8 @@ import org.bitcoins.dlc.wallet.DLCAppConfig import scodec.bits.ByteVector import slick.lifted._ +import java.net.InetSocketAddress +import java.sql.SQLException import java.time.Instant import scala.concurrent.{ExecutionContext, Future} @@ -28,6 +30,9 @@ case class DLCDAO()(implicit override val table: TableQuery[DLCTable] = TableQuery[DLCTable] + private lazy val contactTable: slick.lifted.TableQuery[ + DLCContactDAO#DLCContactTable] = DLCContactDAO().table + override def createAll(ts: Vector[DLCDb]): Future[Vector[DLCDb]] = createAllNoAutoInc(ts, safeDatabase) @@ -118,6 +123,49 @@ case class DLCDAO()(implicit safeDatabase.runVec(q.result) } + def findByContactId(contactId: String): Future[Vector[DLCDb]] = { + val peer: Option[String] = Some(contactId) + val action = table.filter(_.peerOpt === peer).result + safeDatabase.runVec(action) + } + + def updateDLCContactMapping( + dlcId: Sha256Digest, + contcatId: InetSocketAddress): Future[Unit] = { + val contactQuery = contactTable.filter(_.address === contcatId) + + val action = for { + contactExists <- contactQuery.exists.result + _ <- + if (contactExists) DBIO.successful(()) + else DBIO.failed(new SQLException(s"Unknown contact: $contcatId")) + res <- updatePeerAction( + dlcId, + Some(contcatId.getHostName + ":" + contcatId.getPort)) + } yield res + + safeDatabase.run(action).map(_ => ()) + } + + def deleteDLCContactMapping(dlcId: Sha256Digest): Future[Unit] = { + val action = updatePeerAction(dlcId, None) + + safeDatabase.run(action).map(_ => ()) + } + + private def updatePeerAction(dlcId: Sha256Digest, peerOpt: Option[String]) = { + val dlcQuery = table.filter(_.dlcId === dlcId) + + for { + dlcOpt <- dlcQuery.result.headOption + res <- dlcOpt match { + case None => DBIO.failed(new SQLException(s"Unknown DLC: $dlcId")) + case Some(dlc) => + dlcQuery.update(dlc.copy(peerOpt = peerOpt)) + } + } yield res + } + class DLCTable(tag: Tag) extends Table[DLCDb](tag, schemaName, "global_dlc_data") { @@ -162,6 +210,8 @@ case class DLCDAO()(implicit def serializationVersion: Rep[DLCSerializationVersion] = column( "serialization_version") + def peerOpt: Rep[Option[String]] = column("peer") + override def * : ProvenShape[DLCDb] = (dlcId, tempContractId, @@ -179,6 +229,7 @@ case class DLCDAO()(implicit fundingTxIdOpt, closingTxIdOpt, aggregateSignatureOpt, - serializationVersion).<>(DLCDb.tupled, DLCDb.unapply) + serializationVersion, + peerOpt).<>(DLCDb.tupled, DLCDb.unapply) } } diff --git a/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/util/DLCAcceptUtil.scala b/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/util/DLCAcceptUtil.scala index 06dd2a4aa2..0558ebf687 100644 --- a/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/util/DLCAcceptUtil.scala +++ b/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/util/DLCAcceptUtil.scala @@ -115,7 +115,8 @@ object DLCAcceptUtil extends Logging { account: AccountDb, chainType: HDChainType, nextIndex: Int, - contractInfo: ContractInfo): DLCDb = { + contractInfo: ContractInfo, + peerOpt: Option[String]): DLCDb = { DLCDb( dlcId = dlcId, tempContractId = offer.tempContractId, @@ -133,7 +134,8 @@ object DLCAcceptUtil extends Logging { fundingTxIdOpt = None, closingTxIdOpt = None, aggregateSignatureOpt = None, - serializationVersion = contractInfo.serializationVersion + serializationVersion = contractInfo.serializationVersion, + peerOpt = peerOpt ) } diff --git a/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/util/DLCStatusBuilder.scala b/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/util/DLCStatusBuilder.scala index f22c6b9261..a50ea6e87e 100644 --- a/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/util/DLCStatusBuilder.scala +++ b/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/util/DLCStatusBuilder.scala @@ -45,7 +45,8 @@ object DLCStatusBuilder { dlcDb.feeRate, totalCollateral, localCollateral, - payoutAddress + payoutAddress, + dlcDb.peerOpt ) case DLCState.AcceptComputingAdaptorSigs => AcceptedComputingAdaptorSigs( @@ -59,7 +60,8 @@ object DLCStatusBuilder { feeRate = dlcDb.feeRate, totalCollateral = totalCollateral, localCollateral = localCollateral, - payoutAddress + payoutAddress, + dlcDb.peerOpt ) case DLCState.Accepted => Accepted( @@ -73,7 +75,8 @@ object DLCStatusBuilder { dlcDb.feeRate, totalCollateral, localCollateral, - payoutAddress + payoutAddress, + dlcDb.peerOpt ) case DLCState.SignComputingAdaptorSigs => SignedComputingAdaptorSigs( @@ -88,7 +91,8 @@ object DLCStatusBuilder { totalCollateral = totalCollateral, localCollateral = localCollateral, dlcDb.fundingTxIdOpt.get, - payoutAddress + payoutAddress, + dlcDb.peerOpt ) case DLCState.Signed => Signed( @@ -103,7 +107,8 @@ object DLCStatusBuilder { totalCollateral, localCollateral, dlcDb.fundingTxIdOpt.get, - payoutAddress + payoutAddress, + dlcDb.peerOpt ) case DLCState.Broadcasted => Broadcasted( @@ -118,7 +123,8 @@ object DLCStatusBuilder { totalCollateral, localCollateral, dlcDb.fundingTxIdOpt.get, - payoutAddress + payoutAddress, + dlcDb.peerOpt ) case DLCState.Confirmed => Confirmed( @@ -133,7 +139,8 @@ object DLCStatusBuilder { totalCollateral, localCollateral, dlcDb.fundingTxIdOpt.get, - payoutAddress + payoutAddress, + dlcDb.peerOpt ) } @@ -186,7 +193,8 @@ object DLCStatusBuilder { closingTx.txIdBE, myPayout = accounting.myPayout, counterPartyPayout = accounting.theirPayout, - payoutAddress = payoutAddress + payoutAddress = payoutAddress, + peer = dlcDb.peerOpt ) refund case oracleOutcomeState: DLCState.ClosedViaOracleOutcomeState => @@ -214,7 +222,8 @@ object DLCStatusBuilder { oracleOutcome, myPayout = accounting.myPayout, counterPartyPayout = accounting.theirPayout, - payoutAddress = payoutAddress + payoutAddress = payoutAddress, + peer = dlcDb.peerOpt ) case DLCState.RemoteClaimed => RemoteClaimed( @@ -234,7 +243,8 @@ object DLCStatusBuilder { oracleOutcome, myPayout = accounting.myPayout, counterPartyPayout = accounting.theirPayout, - payoutAddress = payoutAddress + payoutAddress = payoutAddress, + peer = dlcDb.peerOpt ) } } diff --git a/docs/applications/server.md b/docs/applications/server.md index 9065dbf180..662b9831c5 100644 --- a/docs/applications/server.md +++ b/docs/applications/server.md @@ -262,15 +262,17 @@ the `-p 9999:9999` port mapping on the docker container to adjust for this. - `collateral` - Satoshis to fund your side of the DLC - `feerate` - Fee rate for both funding and closing transactions, in sats/vbytes - `refundlocktime` - Locktime of the refund transaction - - `--cetlocktime ` - Should not be set unless you know what you are doing. Locktime of the contract execution transactions (defaults to current height) + - `cetlocktime ` - Should not be set unless you know what you are doing. Locktime of the contract execution transactions (defaults to current height) + - `peer` - Peer's network address - `acceptdlc` `offer` `peer` - Accepts a DLC offer given from another party - `offer` - Hex encoded dlc offer message - `peer` - Peer's network address - `acceptdlcoffer` `offer` - Accepts a DLC offer given from another party - `offer` - Hex encoded offer message + - `peer` - Peer's network address - `acceptdlcofferfromfile` `path` `[destination]` - Accepts a DLC offer given from another party - - `path` - Path to dlc offer file - - `destination` - Path to write dlc accept message + - `path` - Path to dlc offer file + - `destination` - Path to write dlc accept message - `contact-add` `alias` `address` `memo` - `alias` - alias for the address like a name - `address` - the tor address for the peer @@ -278,6 +280,9 @@ the `-p 9999:9999` port mapping on the docker container to adjust for this. - `contacts-list` - lists all contacts in the wallet - `contact-remove` `address` - `address` - the tor address for the peer to remove + - `dlc-contact-add` `dlcid` `address` - Associated a DLC with a peer + - `dlc-contact-remove` `dlcid` - Removes a DLC-peer association + - `address` - the tor address for the peer to remove - `signdlc` `accept` - Signs a DLC - `accept` - Hex encoded dlc accept message - `signdlcfromfile` `path` `[destination]` - Signs a DLC @@ -305,6 +310,7 @@ the `-p 9999:9999` port mapping on the docker container to adjust for this. - `canceldlc` `dlcId` - Cancels a DLC and unreserves used utxos - `dlcId` - Internal id of the DLC - `getdlcs` - Returns all dlcs in the wallet + - `address` - optional contact address, if specified the RPC returns only DLCs associated with the given address - `getdlc` `dlcId` - Gets a specific dlc in the wallet - `dlcId` - Internal id of the DLC - `offer-add` `offerTLV` `peerAddress` `message` - Puts an incoming offer into the inbox diff --git a/testkit/src/main/scala/org/bitcoins/testkit/fixtures/DLCDAOFixture.scala b/testkit/src/main/scala/org/bitcoins/testkit/fixtures/DLCDAOFixture.scala index d35851ceab..47e5221246 100644 --- a/testkit/src/main/scala/org/bitcoins/testkit/fixtures/DLCDAOFixture.scala +++ b/testkit/src/main/scala/org/bitcoins/testkit/fixtures/DLCDAOFixture.scala @@ -21,7 +21,8 @@ case class DLCDAOs( dlcSigsDAO: DLCCETSignaturesDAO, dlcRefundSigDAO: DLCRefundSigsDAO, dlcRemoteTxDAO: DLCRemoteTxDAO, - incomingDLCOfferDAO: IncomingDLCOfferDAO) { + incomingDLCOfferDAO: IncomingDLCOfferDAO, + contactDAO: DLCContactDAO) { val list = Vector( announcementDAO, @@ -35,7 +36,8 @@ case class DLCDAOs( dlcSigsDAO, dlcRefundSigDAO, dlcRemoteTxDAO, - incomingDLCOfferDAO + incomingDLCOfferDAO, + contactDAO: DLCContactDAO ) } @@ -54,6 +56,7 @@ trait DLCDAOFixture extends BitcoinSFixture with EmbeddedPg { val dlcRefundSigDAO = DLCRefundSigsDAO() val dlcRemoteTxDAO = DLCRemoteTxDAO() val incomingDLCOfferDAO = IncomingDLCOfferDAO() + val contactDAO = DLCContactDAO() DLCDAOs( announcementDAO = announcementDAO, nonceDAO = nonceDAO, @@ -66,7 +69,8 @@ trait DLCDAOFixture extends BitcoinSFixture with EmbeddedPg { dlcSigsDAO = dlcSigsDAO, dlcRefundSigDAO = dlcRefundSigDAO, dlcRemoteTxDAO = dlcRemoteTxDAO, - incomingDLCOfferDAO = incomingDLCOfferDAO + incomingDLCOfferDAO = incomingDLCOfferDAO, + contactDAO = contactDAO ) } diff --git a/testkit/src/main/scala/org/bitcoins/testkit/wallet/DLCWalletUtil.scala b/testkit/src/main/scala/org/bitcoins/testkit/wallet/DLCWalletUtil.scala index 95111cb681..8b72f27d0b 100644 --- a/testkit/src/main/scala/org/bitcoins/testkit/wallet/DLCWalletUtil.scala +++ b/testkit/src/main/scala/org/bitcoins/testkit/wallet/DLCWalletUtil.scala @@ -276,7 +276,8 @@ object DLCWalletUtil extends Logging { fundingTxIdOpt = None, closingTxIdOpt = None, aggregateSignatureOpt = None, - serializationVersion = DLCSerializationVersion.current + serializationVersion = DLCSerializationVersion.current, + peerOpt = None ) lazy val sampleContractDataDb: DLCContractDataDb = DLCContractDataDb( @@ -310,10 +311,12 @@ object DLCWalletUtil extends Logging { feeRateOpt = Some(SatoshisPerVirtualByte.fromLong(10)), locktime = dummyTimeouts.contractMaturity.toUInt32, refundLocktime = dummyTimeouts.contractTimeout.toUInt32, + peerAddressOpt = None, externalPayoutAddressOpt = payoutAddressAOpt, externalChangeAddressOpt = changeAddressAOpt ) accept <- walletB.acceptDLCOffer(offer, + None, payoutAddressBOpt, changeAddressBOpt) sigs <- walletA.signDLC(accept)