DLC <-> contact mapping (#4346)

* DLC <-> contact mapping

* updated docs

* populate dlc/contact mapping automatically

* typo

* respond to the PR comments

* rename `contact` to `peer`

* fix unit tests

* create a contact when an incoming offers gets created

* drop dlc_contact_mapping table

* fix build

* update the docs

* Revert "update the docs"

This reverts commit 2386adadcd.

* revert dlc-contact-* endpoints
t Please enter the commit message for your changes. Lines starting
This commit is contained in:
rorp 2022-06-14 06:14:28 -07:00 committed by GitHub
parent ddbdde495d
commit fdf281b469
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 670 additions and 145 deletions

View file

@ -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)

View file

@ -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)
}
}
}

View file

@ -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,

View file

@ -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
*/

View file

@ -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 {

View file

@ -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}""")
}
}
}
}

View file

@ -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(

View file

@ -75,7 +75,8 @@ class WalletRoutesSpec
feeRate = null,
totalCollateral = null,
localCollateral = null,
payoutAddress = None
payoutAddress = None,
peer = None
)
(mockWalletApi.findDLCByTemporaryContractId: Sha256Digest => Future[

View file

@ -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")
}
}
}
}
}

View file

@ -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")
}
}
}

View file

@ -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 =>

View file

@ -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 */

View file

@ -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 = ""
)
}

View file

@ -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))
}
}

View file

@ -0,0 +1,7 @@
package org.bitcoins.core.config
object DLC {
val DefaultPort: Int = 2862
}

View file

@ -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
}

View file

@ -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()

View file

@ -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)

View file

@ -37,6 +37,7 @@ class DLCNodeTest extends BitcoinSDLCNodeTest {
UInt32.zero,
UInt32.one,
None,
None,
None)
_ <- nodeB.acceptDLCOffer(addrA, offer.toMessage, None, None)

View file

@ -66,6 +66,7 @@ case class DLCNode(wallet: DLCWalletApi)(implicit
for {
handler <- connectToPeer(peerAddress)
accept <- wallet.acceptDLCOffer(dlcOffer.tlv,
Some(peerAddress),
externalPayoutAddress,
externalChangeAddress)
} yield {

View file

@ -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)
}
}
}

View file

@ -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)

View file

@ -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
)

View file

@ -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
}
}

View file

@ -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(

View file

@ -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");

View file

@ -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");

View file

@ -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 {

View file

@ -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)
}
}

View file

@ -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)

View file

@ -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)
}
}

View file

@ -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
)
}

View file

@ -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
)
}
}

View file

@ -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 <value>` - Should not be set unless you know what you are doing. Locktime of the contract execution transactions (defaults to current height)
- `cetlocktime <value>` - 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

View file

@ -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
)
}

View file

@ -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)