From aeb316988488716780d81e93d442a7129f7db3e3 Mon Sep 17 00:00:00 2001 From: rorp Date: Mon, 7 Mar 2022 12:28:00 -0800 Subject: [PATCH] `getdlcoffer` RPC (#4166) * getdlcoffer RPC * fix build * unit tests --- .../bitcoins/server/WalletRoutesSpec.scala | 62 ++++++++++++++++++- .../bitcoins/server/ServerJsonModels.scala | 20 ++++++ .../org/bitcoins/server/WalletRoutes.scala | 21 +++++++ docs/applications/server.md | 1 + 4 files changed, 102 insertions(+), 2 deletions(-) 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 b717aef695..1530d09b22 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 @@ -1,13 +1,17 @@ package org.bitcoins.server +import akka.http.scaladsl.model.ContentTypes._ import akka.http.scaladsl.testkit.ScalatestRouteTest +import org.bitcoins.core.protocol.dlc.models.DLCMessage.DLCOffer +import org.bitcoins.core.protocol.dlc.models.DLCStatus +import org.bitcoins.core.protocol.tlv.{DLCOfferTLV, LnMessageFactory} +import org.bitcoins.core.wallet.fee.{FeeUnit, SatoshisPerVirtualByte} +import org.bitcoins.crypto.Sha256Digest import org.bitcoins.server.routes.ServerCommand import org.bitcoins.testkit.BitcoinSTestAppConfig import org.bitcoins.wallet.MockWalletApi import org.scalamock.scalatest.MockFactory import org.scalatest.wordspec.AnyWordSpec -import akka.http.scaladsl.model.ContentTypes._ -import org.bitcoins.core.wallet.fee.{FeeUnit, SatoshisPerVirtualByte} import scala.concurrent.Future @@ -36,6 +40,60 @@ class WalletRoutesSpec assert(responseAs[String] == s"""{"result":1,"error":null}""") } } + + "getdlcoffer" in { + + val hex = "a71a006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000fdd82efd00fe00000000000186a0fd" + + "a7101802035945530000000000000000024e4f00000000000186a0fda712d6fdd824d253132dd9798d0a35439d03959989ff099c57eb" + + "68452fd3670413a02028b1366a117470e7a452c379ce1e96c256897499571b412e2561e4781124857edf8237ee3a46bbd9eab0646b12" + + "d2fc402373b4886931e9fcb74dcf8a95e9cc2c620ef3e4fdd8226e00011d9565a789ab074c31dfb7c350203060093224e551903aa9c8" + + "ab5851384fd64461bc2760fdd80609000203594553024e4f3a426974636f696e2061626f7665202435302c303030206f6e2032303231" + + "2d31322d31363a30303a30303a3030555443204020436f696e626173650344f27e85e4a9ac3f7e8c64cf44242134e833c36bf291271" + + "fd135edfce3471d2000160014e90a3dc8e531b0703348fe2e6e11d2fc8de79bed9d6c51aa723e07fd000000000000c3500001fda714f" + + "465f0f3fe17fd6c1000de02000000000101b3a09d05f122a7d3ad466840ce687b9868d84cd6f317eebca76c5e07c39f8ec6000000000" + + "000000000027c6f0b0000000000160014cea305ee870ef0c696543c100c800224f3edf45690d0030000000000160014c7868579385af" + + "4d1108b1583b3e3daf6a643596e024730440220667652339e16bf28e885d0f0503209f8aa96bf6006e35694287180eff37232f002207" + + "fd9e08a93ae8ca8bc88515ecabc91498e534902cc29842b88dc4d3385225951012103f1a388a0b85964c576ee59f408e26a86db60b9a" + + "212ae9dacca91710462b916ef0000000000000001fffffffd006b00000016001476b7c5e332fdc6c7d1075d92589d17f50116bffeb08" + + "eebe8da4c4e72c70af43b6844c7f4000000000000000261bc276061c561e0" + + val lnMessage = LnMessageFactory(DLCOfferTLV).fromHex(hex) + + val offer = DLCOffer.fromMessage(lnMessage) + + val tempContractId = offer.tempContractId + + val dlcId = Sha256Digest.empty + + val status = DLCStatus.Offered( + dlcId = dlcId, + isInitiator = false, + lastUpdated = null, + tempContractId = tempContractId, + contractInfo = null, + timeouts = null, + feeRate = null, + totalCollateral = null, + localCollateral = null + ) + + (mockWalletApi.findDLCByTemporaryContractId: Sha256Digest => Future[ + Option[DLCStatus]]) + .expects(tempContractId) + .returning(Future.successful(Some(status))) + (mockWalletApi.getDLCOffer: Sha256Digest => Future[Option[DLCOffer]]) + .expects(dlcId) + .returning(Future.successful(Some(offer))) + + val route = + walletRoutes.handleCommand( + ServerCommand("getdlcoffer", ujson.Arr(tempContractId.hex))) + + Get() ~> route ~> check { + assert(contentType == `application/json`) + assert(responseAs[String] == s"""{"result":"$hex","error":null}""") + } + } } } 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 94507b2f4e..300a245067 100644 --- a/app/server/src/main/scala/org/bitcoins/server/ServerJsonModels.scala +++ b/app/server/src/main/scala/org/bitcoins/server/ServerJsonModels.scala @@ -1440,6 +1440,26 @@ object OfferSend { } } +case class GetDLCOffer(tempContractId: Sha256Digest) + +object GetDLCOffer { + + def fromJsArr(arr: ujson.Arr): Try[GetDLCOffer] = { + arr.arr.toList match { + case tempContractIdJs :: Nil => + Try { + val tempContractId = Sha256Digest.fromHex(tempContractIdJs.str) + + GetDLCOffer(tempContractId) + } + case other => + val exn = new IllegalArgumentException( + s"Bad number or arguments to offer-send, got=${other.length} expected=1") + Failure(exn) + } + } +} + trait ServerJsonModels { def jsToOracleAnnouncementTLV(js: Value): OracleAnnouncementTLV = 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 1db767f683..221d4b903e 100644 --- a/app/server/src/main/scala/org/bitcoins/server/WalletRoutes.scala +++ b/app/server/src/main/scala/org/bitcoins/server/WalletRoutes.scala @@ -256,6 +256,27 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit } } + case ServerCommand("getdlcoffer", arr) => + GetDLCOffer.fromJsArr(arr) match { + case Failure(exception) => + complete(Server.httpBadRequest(exception)) + case Success(GetDLCOffer(tempContractId)) => + complete { + for { + dlcOpt <- wallet.findDLCByTemporaryContractId(tempContractId) + dlc = dlcOpt.getOrElse( + throw new IllegalArgumentException( + s"Cannot find a DLC with temp contact ID $tempContractId")) + offerOpt <- wallet.getDLCOffer(dlc.dlcId) + offer = offerOpt.getOrElse( + throw new IllegalArgumentException( + s"Cannot find an offer with for DLC ID ${dlc.dlcId}")) + } yield { + Server.httpSuccess(offer.toMessage.hex) + } + } + } + case ServerCommand("getdlcs", _) => complete { wallet.listDLCs().map { dlcs => diff --git a/docs/applications/server.md b/docs/applications/server.md index 229ab2f009..d00057495f 100644 --- a/docs/applications/server.md +++ b/docs/applications/server.md @@ -308,6 +308,7 @@ the `-p 9999:9999` port mapping on the docker container to adjust for this. - `hash` - Hash of the offer TLV - `offer-send` `offerOrTempContractId` `peerAddress` `message` - Sends an offer to a peer. `offerOrTempContractId` is either an offer TLV or a temporary contract ID. - `offers-list` - List all incoming offers from the inbox +- `getdlcoffer` `tempContractId` - Gets a DLC offer by temporary contract ID. ### Network - `getpeers` - List the connected peers