From f77ad289b2cc88c0282e4cc0c80b60aaf05a6e0b Mon Sep 17 00:00:00 2001 From: rorp Date: Wed, 25 May 2022 11:23:47 -0700 Subject: [PATCH] populate dlc/contact mapping automatically --- .../org/bitcoins/server/RoutesSpec.scala | 6 +- .../bitcoins/server/ServerJsonModels.scala | 78 +++++++++++----- .../org/bitcoins/server/WalletRoutes.scala | 16 +++- .../core/api/dlc/wallet/DLCWalletApi.scala | 21 ++--- .../dlc/node/DLCNegotiationTest.scala | 4 +- .../org/bitcoins/dlc/node/DLCNodeTest.scala | 1 + .../scala/org/bitcoins/dlc/node/DLCNode.scala | 1 + .../dlc/wallet/DLCExecutionTest.scala | 5 +- .../dlc/wallet/MultiWalletDLCTest.scala | 2 + .../dlc/wallet/WalletDLCSetupTest.scala | 90 ++++++++++++++----- .../internal/DLCDataManagementTest.scala | 6 +- .../org/bitcoins/dlc/wallet/DLCWallet.scala | 18 +++- .../wallet/models/DLCContactMappingDAO.scala | 26 ++++++ docs/applications/server.md | 8 +- .../testkit/wallet/DLCWalletUtil.scala | 2 + 15 files changed, 216 insertions(+), 68 deletions(-) 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 33c12d32c0..63e9a63797 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/src/main/scala/org/bitcoins/server/ServerJsonModels.scala b/app/server/src/main/scala/org/bitcoins/server/ServerJsonModels.scala index 7ee57c35fd..11aab59aeb 100644 --- a/app/server/src/main/scala/org/bitcoins/server/ServerJsonModels.scala +++ b/app/server/src/main/scala/org/bitcoins/server/ServerJsonModels.scala @@ -15,11 +15,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._ @@ -658,10 +656,7 @@ object GetDLCs extends ServerJsonModels { jsArr.arr.toList match { case addressJs :: Nil => Try { - val address = { - val uri = new URI(s"tcp://${addressJs.str}") - InetSocketAddress.createUnresolved(uri.getHost, uri.getPort) - } + val address = jsToInetSocketAddress(addressJs) GetDLCs(Some(address)) } case Nil => @@ -702,7 +697,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 { @@ -715,7 +711,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) @@ -728,13 +725,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 { @@ -745,6 +746,7 @@ object CreateDLCOffer extends ServerJsonModels { locktimeJs, refundLTJs, Null, + Null, Null) case contractInfoJs :: collateralJs :: feeRateOptJs :: locktimeJs :: refundLTJs :: payoutAddressJs :: Nil => parseParameters(contractInfoJs, @@ -753,6 +755,7 @@ object CreateDLCOffer extends ServerJsonModels { locktimeJs, refundLTJs, payoutAddressJs, + Null, Null) case contractInfoJs :: collateralJs :: feeRateOptJs :: locktimeJs :: refundLTJs :: payoutAddressJs :: changeAddressJs :: Nil => parseParameters(contractInfoJs, @@ -761,7 +764,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( @@ -929,7 +942,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 { @@ -937,7 +951,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 = @@ -945,16 +960,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")) @@ -987,9 +1009,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)) @@ -1415,7 +1435,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 { @@ -1459,14 +1479,13 @@ 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) + val peerAddress = jsToInetSocketAddress(peerAddressJs, 2862) val message = messageJs.str val offerE = Try(LnMessageFactory(DLCOfferTLV).fromHex(offerJs.str).tlv) @@ -1622,4 +1641,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 a6230d3190..200acc5564 100644 --- a/app/server/src/main/scala/org/bitcoins/server/WalletRoutes.scala +++ b/app/server/src/main/scala/org/bitcoins/server/WalletRoutes.scala @@ -355,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) @@ -376,6 +377,7 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit feeRateOpt, locktime, refundLT, + peerAddressOpt, payoutAddressOpt, changeAddressOpt) case None => @@ -384,6 +386,7 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit collateral, feeRateOpt, refundLT, + peerAddressOpt, payoutAddressOpt, changeAddressOpt) } @@ -399,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) } @@ -426,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 b8322fbb90..81e6a8ff9e 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] 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/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..488943f58c 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 @@ -1,5 +1,6 @@ package org.bitcoins.dlc.wallet +import org.bitcoins.core.api.dlc.wallet.db.DLCContactDb import org.bitcoins.core.currency._ import org.bitcoins.core.number.{UInt32, UInt64} import org.bitcoins.core.protocol.BitcoinAddress @@ -20,6 +21,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 +49,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { offerData.timeouts.contractMaturity.toUInt32, offerData.timeouts.contractTimeout.toUInt32, None, + None, None ) dlcId = calcDLCId(offer.fundingInputs.map(_.outPoint)) @@ -64,7 +67,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 +192,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 +252,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 +284,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { offerData.timeouts.contractMaturity.toUInt32, offerData.timeouts.contractTimeout.toUInt32, None, + None, None ) dlcId = calcDLCId(offer.fundingInputs.map(_.outPoint)) @@ -295,7 +301,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 +380,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 +527,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { offerData.timeouts.contractMaturity.toUInt32, offerData.timeouts.contractTimeout.toUInt32, None, + None, None ) @@ -565,9 +573,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 +617,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 +662,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 +698,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 +766,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { offerData.timeouts.contractMaturity.toUInt32, offerData.timeouts.contractTimeout.toUInt32, None, + None, None ) _ = { @@ -768,7 +781,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 +864,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { feeRateOpt = feeRateOpt, locktime = UInt32.zero, refundLT = UInt32.one, + peerAddressOpt = None, externalPayoutAddressOpt = None, externalChangeAddressOpt = None ) @@ -860,8 +874,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 +913,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { feeRateOpt = feeRateOpt, locktime = UInt32.zero, refundLT = UInt32.one, + peerAddressOpt = None, externalPayoutAddressOpt = None, externalChangeAddressOpt = None ) @@ -906,8 +921,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 +945,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { feeRateOpt = feeRateOpt, locktime = UInt32.zero, refundLT = UInt32.one, + peerAddressOpt = None, externalPayoutAddressOpt = None, externalChangeAddressOpt = None ) @@ -937,8 +953,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 +974,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 +998,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest { offerData.timeouts.contractMaturity.toUInt32, UInt32.max, None, + None, None )) } yield { @@ -1006,12 +1024,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,31 +1061,58 @@ 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)) + + val contactA = DLCContactDb("A", peerAddressOpt1.get, "memo") + val contactB = DLCContactDb("B", peerAddressOpt2.get, "memo") for { + _ <- walletA.contactDAO.create(contactA) + _ <- walletB.contactDAO.create(contactB) offer <- walletA.createDLCOffer( contractInfoTLV = contractInfo, collateral = totalCollateral, feeRateOpt = feeRateOpt, locktime = UInt32.zero, refundLT = UInt32.one, + peerAddressOpt = peerAddressOpt1, externalPayoutAddressOpt = payoutAddressAOpt, externalChangeAddressOpt = changeAddressAOpt ) + dlcContactA <- walletA.dlcContactMappingDAO.read(offer.dlcId) accept <- walletB.acceptDLCOffer(offer, + peerAddressOpt2, payoutAddressBOpt, changeAddressBOpt) + dlcContactB <- walletB.dlcContactMappingDAO.read(offer.dlcId) offer1 <- walletA.createDLCOffer( contractInfoTLV = contractInfo1, collateral = totalCollateral1, feeRateOpt = feeRateOpt1, locktime = UInt32.zero, refundLT = UInt32.one, + peerAddressOpt = peerAddressOpt3, externalPayoutAddressOpt = None, externalChangeAddressOpt = None ) - accept1 <- walletB.acceptDLCOffer(offer1, None, None) + dlcContactA1 <- walletA.dlcContactMappingDAO.read(offer1.dlcId) + accept1 <- walletB.acceptDLCOffer(offer1, peerAddressOpt3, None, None) + dlcContactB1 <- walletB.dlcContactMappingDAO.read(offer1.dlcId) } yield { + assert(dlcContactA.nonEmpty) + assert(dlcContactA.get.contactId == contactA.address) + assert(dlcContactA1.isEmpty) + + assert(dlcContactB.nonEmpty) + assert(dlcContactB.get.contactId == contactB.address) + assert(dlcContactB1.isEmpty) + + assert(offer) assert(offer.pubKeys.payoutAddress == payoutAddressAOpt.get) assert(offer.changeAddress == changeAddressAOpt.get) assert(accept.pubKeys.payoutAddress == payoutAddressBOpt.get) @@ -1101,10 +1147,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 +1162,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/scala/org/bitcoins/dlc/wallet/DLCWallet.scala b/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/DLCWallet.scala index 67297f7392..e8d1bbb49a 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 @@ -277,6 +277,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 => @@ -285,6 +286,7 @@ abstract class DLCWallet feeRateOpt, locktime = UInt32(height), refundLT, + peerAddressOpt, externalPayoutAddressOpt, externalChangeAddressOpt) } @@ -301,6 +303,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") @@ -478,6 +481,11 @@ abstract class DLCWallet dlcOfferDb = dlcOfferDb) _ <- safeDatabase.run(offerActions) + _ <- peerAddressOpt match { + case Some(a) => + dlcContactMappingDAO.createIfContactExists(dlcDb.dlcId, a) + case None => Future.successful(None) + } status <- findDLC(dlcId) _ <- dlcConfig.walletCallbacks.executeOnDLCStateChange(logger, status.get) } yield offer @@ -666,6 +674,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") @@ -697,6 +706,7 @@ abstract class DLCWallet case None => createNewDLCAccept(collateral, offer, + peerAddressOpt, externalPayoutAddressOpt, externalChangeAddressOpt) } @@ -748,6 +758,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 { @@ -802,6 +813,11 @@ abstract class DLCWallet isExternalAddress <- addressDAO .findAddress(initializedAccept.pubKeys.payoutAddress) .map(_.isEmpty) + contactOpt <- peerAddressOpt match { + case Some(a) => + dlcContactMappingDAO.createIfContactExists(offer.dlcId, a) + case None => Future.successful(None) + } status = DLCStatusBuilder.buildInProgressDLCStatus( dlcDb = initializedAccept.dlc, contractInfo = offer.contractInfo, @@ -810,7 +826,7 @@ abstract class DLCWallet payoutAddress = Some( PayoutAddress(initializedAccept.pubKeys.payoutAddress, isExternalAddress)), - contactOpt = None + contactOpt = contactOpt ) _ = dlcConfig.walletCallbacks.executeOnDLCStateChange(logger, status) cetSigs <- signer.createCETSigsAsync() diff --git a/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/models/DLCContactMappingDAO.scala b/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/models/DLCContactMappingDAO.scala index 302d12ef53..981e78b6af 100644 --- a/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/models/DLCContactMappingDAO.scala +++ b/dlc-wallet/src/main/scala/org/bitcoins/dlc/wallet/models/DLCContactMappingDAO.scala @@ -61,6 +61,32 @@ case class DLCContactMappingDAO()(implicit create(DLCContactMappingDb(dlcId, contactId)) } + def createIfContactExists( + dlcId: Sha256Digest, + contactId: InetSocketAddress): Future[Option[DLCContactDb]] = { + val action = for { + contactOpt <- contactTable + .filter(_.address === contactId) + .result + .headOption + res <- + if (contactOpt.nonEmpty) { + val db = DLCContactMappingDb(dlcId, contactId) + (table += db).map(_ => contactOpt) + } else { + DBIO.successful(None) + } + } yield res + + safeDatabase.run(action) + } + + def upsert( + dlcId: Sha256Digest, + contactId: InetSocketAddress): Future[DLCContactMappingDb] = { + upsert(DLCContactMappingDb(dlcId, contactId)) + } + def delete(dlcId: Sha256Digest): Future[Unit] = { safeDatabase.run(table.filter(_.dlcId === dlcId).delete).map(_ => ()) } diff --git a/docs/applications/server.md b/docs/applications/server.md index 49ce531b72..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 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..417fae5bd0 100644 --- a/testkit/src/main/scala/org/bitcoins/testkit/wallet/DLCWalletUtil.scala +++ b/testkit/src/main/scala/org/bitcoins/testkit/wallet/DLCWalletUtil.scala @@ -310,10 +310,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)