mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-13 19:37:30 +01:00
DLC <-> contact mapping
This commit is contained in:
parent
2af7923f3b
commit
cfee8cf7e4
20 changed files with 725 additions and 72 deletions
|
@ -2,6 +2,7 @@ package org.bitcoins.commons
|
|||
|
||||
import org.bitcoins.commons.serializers.Picklers
|
||||
import org.bitcoins.commons.serializers.Picklers._
|
||||
import org.bitcoins.core.api.dlc.wallet.db.DLCContactDb
|
||||
import org.bitcoins.core.currency.{CurrencyUnit, Satoshis}
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.protocol.dlc.models.DLCMessage._
|
||||
|
@ -17,6 +18,8 @@ import org.bitcoins.testkitcore.util.BitcoinSJvmTest
|
|||
import org.scalacheck.Gen
|
||||
import upickle.default._
|
||||
|
||||
import java.net.InetSocketAddress
|
||||
|
||||
class DLCStatusTest extends BitcoinSJvmTest {
|
||||
behavior of "DLCStatus"
|
||||
|
||||
|
@ -29,6 +32,12 @@ class DLCStatusTest extends BitcoinSJvmTest {
|
|||
|
||||
val payoutAddress = Option.empty[PayoutAddress]
|
||||
|
||||
val contact = Some(
|
||||
DLCContactDb(address =
|
||||
InetSocketAddress.createUnresolved("127.0.0.1", 0),
|
||||
alias = "alias",
|
||||
memo = "memo"))
|
||||
|
||||
val status =
|
||||
DLCStatus.Offered(
|
||||
Sha256Digest.empty,
|
||||
|
@ -40,7 +49,8 @@ class DLCStatusTest extends BitcoinSJvmTest {
|
|||
offer.feeRate,
|
||||
totalCollateral,
|
||||
offer.collateral,
|
||||
payoutAddress
|
||||
payoutAddress,
|
||||
contact
|
||||
)
|
||||
|
||||
assert(status.state == DLCState.Offered)
|
||||
|
@ -66,6 +76,8 @@ class DLCStatusTest extends BitcoinSJvmTest {
|
|||
"tb1q4ps6c9ewa7uca5v39fakykq9q6hpgjkxje8gve"),
|
||||
true))
|
||||
|
||||
val contact = Option.empty[DLCContactDb]
|
||||
|
||||
val status =
|
||||
DLCStatus.Accepted(
|
||||
Sha256Digest.empty,
|
||||
|
@ -78,7 +90,8 @@ class DLCStatusTest extends BitcoinSJvmTest {
|
|||
offer.feeRate,
|
||||
totalCollateral,
|
||||
offer.collateral,
|
||||
payoutAddress
|
||||
payoutAddress,
|
||||
contact
|
||||
)
|
||||
|
||||
assert(status.state == DLCState.Accepted)
|
||||
|
@ -100,6 +113,8 @@ class DLCStatusTest extends BitcoinSJvmTest {
|
|||
|
||||
val payoutAddress = Option.empty[PayoutAddress]
|
||||
|
||||
val contact = Option.empty[DLCContactDb]
|
||||
|
||||
val status =
|
||||
DLCStatus.Signed(
|
||||
Sha256Digest.empty,
|
||||
|
@ -113,7 +128,8 @@ class DLCStatusTest extends BitcoinSJvmTest {
|
|||
totalCollateral,
|
||||
offer.collateral,
|
||||
txId,
|
||||
payoutAddress
|
||||
payoutAddress,
|
||||
contact
|
||||
)
|
||||
|
||||
assert(status.state == DLCState.Signed)
|
||||
|
@ -135,6 +151,8 @@ class DLCStatusTest extends BitcoinSJvmTest {
|
|||
|
||||
val payoutAddress = Option.empty[PayoutAddress]
|
||||
|
||||
val contact = Option.empty[DLCContactDb]
|
||||
|
||||
val status =
|
||||
DLCStatus.Broadcasted(
|
||||
Sha256Digest.empty,
|
||||
|
@ -148,7 +166,8 @@ class DLCStatusTest extends BitcoinSJvmTest {
|
|||
totalCollateral,
|
||||
offer.collateral,
|
||||
fundingTxId,
|
||||
payoutAddress
|
||||
payoutAddress,
|
||||
contact
|
||||
)
|
||||
|
||||
assert(status.state == DLCState.Broadcasted)
|
||||
|
@ -170,6 +189,8 @@ class DLCStatusTest extends BitcoinSJvmTest {
|
|||
|
||||
val payoutAddress = Option.empty[PayoutAddress]
|
||||
|
||||
val contact = Option.empty[DLCContactDb]
|
||||
|
||||
val status =
|
||||
DLCStatus.Confirmed(
|
||||
Sha256Digest.empty,
|
||||
|
@ -183,7 +204,8 @@ class DLCStatusTest extends BitcoinSJvmTest {
|
|||
totalCollateral,
|
||||
offer.collateral,
|
||||
fundingTxId,
|
||||
payoutAddress
|
||||
payoutAddress,
|
||||
contact = contact
|
||||
)
|
||||
|
||||
assert(status.state == DLCState.Confirmed)
|
||||
|
@ -216,6 +238,8 @@ class DLCStatusTest extends BitcoinSJvmTest {
|
|||
|
||||
val payoutAddress = Option.empty[PayoutAddress]
|
||||
|
||||
val contact = Option.empty[DLCContactDb]
|
||||
|
||||
val status =
|
||||
DLCStatus.Claimed(
|
||||
Sha256Digest.empty,
|
||||
|
@ -234,7 +258,8 @@ class DLCStatusTest extends BitcoinSJvmTest {
|
|||
outcome,
|
||||
myPayout = myPayout,
|
||||
counterPartyPayout = theirPayout,
|
||||
payoutAddress = payoutAddress
|
||||
payoutAddress = payoutAddress,
|
||||
contact = contact
|
||||
)
|
||||
|
||||
assert(status.state == DLCState.Claimed)
|
||||
|
@ -270,6 +295,8 @@ class DLCStatusTest extends BitcoinSJvmTest {
|
|||
|
||||
val payoutAddress = Option.empty[PayoutAddress]
|
||||
|
||||
val contact = Option.empty[DLCContactDb]
|
||||
|
||||
val status =
|
||||
DLCStatus.RemoteClaimed(
|
||||
Sha256Digest.empty,
|
||||
|
@ -288,7 +315,8 @@ class DLCStatusTest extends BitcoinSJvmTest {
|
|||
outcome,
|
||||
myPayout = myPayout,
|
||||
counterPartyPayout = theirPayout,
|
||||
payoutAddress = payoutAddress
|
||||
payoutAddress = payoutAddress,
|
||||
contact = contact
|
||||
)
|
||||
|
||||
assert(status.state == DLCState.RemoteClaimed)
|
||||
|
@ -320,6 +348,8 @@ class DLCStatusTest extends BitcoinSJvmTest {
|
|||
|
||||
val payoutAddress = Option.empty[PayoutAddress]
|
||||
|
||||
val contact = Option.empty[DLCContactDb]
|
||||
|
||||
val status =
|
||||
DLCStatus.Refunded(
|
||||
Sha256Digest.empty,
|
||||
|
@ -336,7 +366,8 @@ class DLCStatusTest extends BitcoinSJvmTest {
|
|||
closingTxId,
|
||||
myPayout = myPayout,
|
||||
counterPartyPayout = theirPayout,
|
||||
payoutAddress = payoutAddress
|
||||
payoutAddress = payoutAddress,
|
||||
contact = contact
|
||||
)
|
||||
|
||||
assert(status.state == DLCState.Refunded)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -815,7 +815,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 +842,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),
|
||||
"contact" -> writeJs(contact)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -859,7 +866,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),
|
||||
"contact" -> writeJs(contact)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -881,7 +889,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),
|
||||
"contact" -> writeJs(contact)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -905,7 +914,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),
|
||||
"contact" -> writeJs(contact)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -928,7 +938,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),
|
||||
"contact" -> writeJs(contact)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -952,7 +963,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),
|
||||
"contact" -> writeJs(contact)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -976,7 +988,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),
|
||||
"contact" -> writeJs(contact)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1019,7 +1032,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),
|
||||
"contact" -> writeJs(contact)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1062,7 +1076,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),
|
||||
"contact" -> writeJs(contact)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1091,7 +1106,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),
|
||||
"contact" -> writeJs(contact)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1163,6 +1179,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 contactOpt =
|
||||
if (obj("contact").isNull) None
|
||||
else Some(readContactDb(obj("contact").obj))
|
||||
|
||||
lazy val contractId = ByteVector.fromValidHex(obj("contractId").str)
|
||||
lazy val fundingTxId = DoubleSha256DigestBE(obj("fundingTxId").str)
|
||||
|
@ -1232,7 +1251,8 @@ object Picklers {
|
|||
feeRate,
|
||||
totalCollateral,
|
||||
localCollateral,
|
||||
payoutAddress
|
||||
payoutAddress,
|
||||
contactOpt
|
||||
)
|
||||
case DLCState.AcceptComputingAdaptorSigs =>
|
||||
AcceptedComputingAdaptorSigs(
|
||||
|
@ -1246,7 +1266,8 @@ object Picklers {
|
|||
feeRate,
|
||||
totalCollateral,
|
||||
localCollateral,
|
||||
payoutAddress
|
||||
payoutAddress,
|
||||
contactOpt
|
||||
)
|
||||
case DLCState.Accepted =>
|
||||
Accepted(
|
||||
|
@ -1260,7 +1281,8 @@ object Picklers {
|
|||
feeRate,
|
||||
totalCollateral,
|
||||
localCollateral,
|
||||
payoutAddress
|
||||
payoutAddress,
|
||||
contactOpt
|
||||
)
|
||||
case DLCState.SignComputingAdaptorSigs =>
|
||||
SignedComputingAdaptorSigs(
|
||||
|
@ -1275,7 +1297,8 @@ object Picklers {
|
|||
totalCollateral,
|
||||
localCollateral,
|
||||
fundingTxId,
|
||||
payoutAddress
|
||||
payoutAddress,
|
||||
contactOpt
|
||||
)
|
||||
case DLCState.Signed =>
|
||||
Signed(
|
||||
|
@ -1290,7 +1313,8 @@ object Picklers {
|
|||
totalCollateral,
|
||||
localCollateral,
|
||||
fundingTxId,
|
||||
payoutAddress
|
||||
payoutAddress,
|
||||
contactOpt
|
||||
)
|
||||
case DLCState.Broadcasted =>
|
||||
Broadcasted(
|
||||
|
@ -1305,7 +1329,8 @@ object Picklers {
|
|||
totalCollateral,
|
||||
localCollateral,
|
||||
fundingTxId,
|
||||
payoutAddress
|
||||
payoutAddress,
|
||||
contactOpt
|
||||
)
|
||||
case DLCState.Confirmed =>
|
||||
Confirmed(
|
||||
|
@ -1320,7 +1345,8 @@ object Picklers {
|
|||
totalCollateral,
|
||||
localCollateral,
|
||||
fundingTxId,
|
||||
payoutAddress
|
||||
payoutAddress,
|
||||
contactOpt
|
||||
)
|
||||
case DLCState.Claimed =>
|
||||
Claimed(
|
||||
|
@ -1340,7 +1366,8 @@ object Picklers {
|
|||
oracleOutcome,
|
||||
myPayout = myPayoutOpt.get,
|
||||
counterPartyPayout = theirPayoutOpt.get,
|
||||
payoutAddress
|
||||
payoutAddress,
|
||||
contactOpt
|
||||
)
|
||||
case DLCState.RemoteClaimed =>
|
||||
require(oracleSigs.size == 1,
|
||||
|
@ -1362,7 +1389,8 @@ object Picklers {
|
|||
oracleOutcome,
|
||||
myPayout = myPayoutOpt.get,
|
||||
counterPartyPayout = theirPayoutOpt.get,
|
||||
payoutAddress
|
||||
payoutAddress,
|
||||
contactOpt
|
||||
)
|
||||
case DLCState.Refunded =>
|
||||
Refunded(
|
||||
|
@ -1380,7 +1408,8 @@ object Picklers {
|
|||
closingTxId,
|
||||
myPayout = myPayoutOpt.get,
|
||||
counterPartyPayout = theirPayoutOpt.get,
|
||||
payoutAddress
|
||||
payoutAddress,
|
||||
contactOpt
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,42 @@ 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":"ok","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}""")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,7 +75,8 @@ class WalletRoutesSpec
|
|||
feeRate = null,
|
||||
totalCollateral = null,
|
||||
localCollateral = null,
|
||||
payoutAddress = None
|
||||
payoutAddress = None,
|
||||
contact = None
|
||||
)
|
||||
|
||||
(mockWalletApi.findDLCByTemporaryContractId: Sha256Digest => Future[
|
||||
|
|
|
@ -3,7 +3,12 @@ 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.{
|
||||
ContactAdd,
|
||||
ContactRemove,
|
||||
DLCContactAdd,
|
||||
DLCContactRemove
|
||||
}
|
||||
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 +164,28 @@ 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 { _ =>
|
||||
Server.httpSuccess("ok")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case ServerCommand("dlc-contact-remove", arr) =>
|
||||
withValidServerCommand(DLCContactRemove.fromJsArr(arr)) {
|
||||
dlcContactRemove =>
|
||||
complete {
|
||||
dlcNode.wallet
|
||||
.removeDLCContactMapping(dlcContactRemove.dlcId)
|
||||
.map { _ =>
|
||||
Server.httpSuccess("ok")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -650,6 +650,30 @@ 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 = {
|
||||
val uri = new URI(s"tcp://${addressJs.str}")
|
||||
InetSocketAddress.createUnresolved(uri.getHost, uri.getPort)
|
||||
}
|
||||
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 {
|
||||
|
|
|
@ -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) =>
|
||||
|
|
|
@ -170,6 +170,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 both Neutrino and SPV methods of syncing */
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.bitcoins.core.protocol.dlc.models
|
||||
|
||||
import org.bitcoins.core.api.dlc.wallet.db.DLCContactDb
|
||||
import org.bitcoins.core.currency.CurrencyUnit
|
||||
import org.bitcoins.core.dlc.accounting.{DLCAccounting, RateOfReturnUtil}
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
|
@ -35,6 +36,7 @@ sealed trait DLCStatus {
|
|||
def localCollateral: CurrencyUnit
|
||||
def remoteCollateral: CurrencyUnit = totalCollateral - localCollateral
|
||||
def payoutAddress: Option[PayoutAddress]
|
||||
def contact: Option[DLCContactDb]
|
||||
|
||||
lazy val announcements: Vector[OracleAnnouncementTLV] = {
|
||||
oracleInfos.flatMap(_.singleOracleInfos.map(_.announcement))
|
||||
|
@ -92,7 +94,8 @@ object DLCStatus {
|
|||
feeRate: FeeUnit,
|
||||
totalCollateral: CurrencyUnit,
|
||||
localCollateral: CurrencyUnit,
|
||||
payoutAddress: Option[PayoutAddress]
|
||||
payoutAddress: Option[PayoutAddress],
|
||||
contact: Option[DLCContactDb]
|
||||
) extends DLCStatus {
|
||||
override val state: DLCState.Offered.type = DLCState.Offered
|
||||
}
|
||||
|
@ -108,7 +111,8 @@ object DLCStatus {
|
|||
feeRate: FeeUnit,
|
||||
totalCollateral: CurrencyUnit,
|
||||
localCollateral: CurrencyUnit,
|
||||
payoutAddress: Option[PayoutAddress])
|
||||
payoutAddress: Option[PayoutAddress],
|
||||
contact: Option[DLCContactDb])
|
||||
extends AcceptedDLCStatus {
|
||||
|
||||
override val state: DLCState.AcceptComputingAdaptorSigs.type =
|
||||
|
@ -126,7 +130,8 @@ object DLCStatus {
|
|||
feeRate: FeeUnit,
|
||||
totalCollateral: CurrencyUnit,
|
||||
localCollateral: CurrencyUnit,
|
||||
payoutAddress: Option[PayoutAddress])
|
||||
payoutAddress: Option[PayoutAddress],
|
||||
contact: Option[DLCContactDb])
|
||||
extends AcceptedDLCStatus {
|
||||
override val state: DLCState.Accepted.type = DLCState.Accepted
|
||||
}
|
||||
|
@ -143,7 +148,8 @@ object DLCStatus {
|
|||
totalCollateral: CurrencyUnit,
|
||||
localCollateral: CurrencyUnit,
|
||||
fundingTxId: DoubleSha256DigestBE,
|
||||
payoutAddress: Option[PayoutAddress])
|
||||
payoutAddress: Option[PayoutAddress],
|
||||
contact: Option[DLCContactDb])
|
||||
extends SignedDLCStatus {
|
||||
|
||||
override val state: DLCState.SignComputingAdaptorSigs.type =
|
||||
|
@ -162,7 +168,8 @@ object DLCStatus {
|
|||
totalCollateral: CurrencyUnit,
|
||||
localCollateral: CurrencyUnit,
|
||||
fundingTxId: DoubleSha256DigestBE,
|
||||
payoutAddress: Option[PayoutAddress])
|
||||
payoutAddress: Option[PayoutAddress],
|
||||
contact: Option[DLCContactDb])
|
||||
extends SignedDLCStatus {
|
||||
override val state: DLCState.Signed.type = DLCState.Signed
|
||||
}
|
||||
|
@ -179,7 +186,8 @@ object DLCStatus {
|
|||
totalCollateral: CurrencyUnit,
|
||||
localCollateral: CurrencyUnit,
|
||||
fundingTxId: DoubleSha256DigestBE,
|
||||
payoutAddress: Option[PayoutAddress])
|
||||
payoutAddress: Option[PayoutAddress],
|
||||
contact: Option[DLCContactDb])
|
||||
extends SignedDLCStatus {
|
||||
override val state: DLCState.Broadcasted.type = DLCState.Broadcasted
|
||||
}
|
||||
|
@ -196,7 +204,8 @@ object DLCStatus {
|
|||
totalCollateral: CurrencyUnit,
|
||||
localCollateral: CurrencyUnit,
|
||||
fundingTxId: DoubleSha256DigestBE,
|
||||
payoutAddress: Option[PayoutAddress])
|
||||
payoutAddress: Option[PayoutAddress],
|
||||
contact: Option[DLCContactDb])
|
||||
extends SignedDLCStatus {
|
||||
override val state: DLCState.Confirmed.type = DLCState.Confirmed
|
||||
}
|
||||
|
@ -218,7 +227,8 @@ object DLCStatus {
|
|||
oracleOutcome: OracleOutcome,
|
||||
myPayout: CurrencyUnit,
|
||||
counterPartyPayout: CurrencyUnit,
|
||||
payoutAddress: Option[PayoutAddress])
|
||||
payoutAddress: Option[PayoutAddress],
|
||||
contact: Option[DLCContactDb])
|
||||
extends ClaimedDLCStatus {
|
||||
override val state: DLCState.Claimed.type = DLCState.Claimed
|
||||
}
|
||||
|
@ -240,7 +250,8 @@ object DLCStatus {
|
|||
oracleOutcome: OracleOutcome,
|
||||
myPayout: CurrencyUnit,
|
||||
counterPartyPayout: CurrencyUnit,
|
||||
payoutAddress: Option[PayoutAddress])
|
||||
payoutAddress: Option[PayoutAddress],
|
||||
contact: Option[DLCContactDb])
|
||||
extends ClaimedDLCStatus {
|
||||
override val state: DLCState.RemoteClaimed.type = DLCState.RemoteClaimed
|
||||
override val oracleSigs: Vector[SchnorrDigitalSignature] = Vector(oracleSig)
|
||||
|
@ -261,7 +272,8 @@ object DLCStatus {
|
|||
closingTxId: DoubleSha256DigestBE,
|
||||
myPayout: CurrencyUnit,
|
||||
counterPartyPayout: CurrencyUnit,
|
||||
payoutAddress: Option[PayoutAddress])
|
||||
payoutAddress: Option[PayoutAddress],
|
||||
contact: Option[DLCContactDb])
|
||||
extends ClosedDLCStatus {
|
||||
override val state: DLCState.Refunded.type = DLCState.Refunded
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -0,0 +1,217 @@
|
|||
package org.bitcoins.dlc.wallet
|
||||
|
||||
import org.bitcoins.core.api.dlc.wallet.db.{DLCContactDb, DLCDb}
|
||||
import org.bitcoins.crypto.Sha256Digest
|
||||
import org.bitcoins.dlc.wallet.models.{DLCContactMapping, DLCContactMappingDb}
|
||||
import org.bitcoins.testkit.fixtures.DLCDAOFixture
|
||||
import org.bitcoins.testkit.wallet.{BitcoinSWalletTest, DLCWalletUtil}
|
||||
|
||||
import java.net.InetSocketAddress
|
||||
import java.sql.SQLException
|
||||
|
||||
class DLCContactMappingDAOTest extends BitcoinSWalletTest with DLCDAOFixture {
|
||||
|
||||
behavior of "DLCContactMappingDAO"
|
||||
|
||||
val dlcDb1: DLCDb = DLCWalletUtil.sampleDLCDb
|
||||
|
||||
val dlcDb2: DLCDb = DLCWalletUtil.sampleDLCDb.copy(
|
||||
dlcId = Sha256Digest.fromBytes(dlcDb1.dlcId.bytes.reverse),
|
||||
tempContractId =
|
||||
Sha256Digest.fromBytes(dlcDb1.tempContractId.bytes.reverse))
|
||||
|
||||
val address1 = InetSocketAddress.createUnresolved("127.0.0.1", 1)
|
||||
val address2 = InetSocketAddress.createUnresolved("127.0.0.1", 2)
|
||||
|
||||
val contactDb1: DLCContactDb = DLCContactDb(
|
||||
address = address1,
|
||||
alias = "1",
|
||||
memo = "memo 1"
|
||||
)
|
||||
|
||||
val contactDb2: DLCContactDb = DLCContactDb(
|
||||
address = address2,
|
||||
alias = "2",
|
||||
memo = "memo 2"
|
||||
)
|
||||
|
||||
it should "insert rows and enforce foreign keys" in { daos =>
|
||||
val dao = daos.dlcContactMappingDAO
|
||||
|
||||
for {
|
||||
// no such DLC
|
||||
_ <- recoverToSucceededIf[SQLException](
|
||||
dao.create(
|
||||
DLCContactMappingDb(
|
||||
dlcDb1.dlcId,
|
||||
contactDb1.address
|
||||
)))
|
||||
|
||||
_ <- daos.dlcDAO.create(dlcDb1)
|
||||
|
||||
// no such contact
|
||||
_ <- recoverToSucceededIf[SQLException](
|
||||
dao.create(
|
||||
DLCContactMappingDb(
|
||||
dlcDb1.dlcId,
|
||||
contactDb1.address
|
||||
)))
|
||||
|
||||
_ <- daos.contactDAO.create(contactDb1)
|
||||
|
||||
// both DLC and contact exist
|
||||
created <- dao.create(
|
||||
DLCContactMappingDb(
|
||||
dlcDb1.dlcId,
|
||||
contactDb1.address
|
||||
))
|
||||
read <- dao.read(dlcDb1.dlcId)
|
||||
} yield {
|
||||
assert(read.isDefined)
|
||||
assert(read.get == created)
|
||||
assert(created.dlcId == dlcDb1.dlcId)
|
||||
assert(created.contactId == contactDb1.address)
|
||||
}
|
||||
}
|
||||
|
||||
it should "update rows and enforce foreign keys" in { daos =>
|
||||
val dao = daos.dlcContactMappingDAO
|
||||
|
||||
for {
|
||||
_ <- daos.dlcDAO.create(dlcDb1)
|
||||
_ <- daos.contactDAO.create(contactDb1)
|
||||
_ <- dao.create(
|
||||
DLCContactMappingDb(
|
||||
dlcDb1.dlcId,
|
||||
contactDb1.address
|
||||
))
|
||||
|
||||
// no such contact
|
||||
_ <- recoverToSucceededIf[SQLException](
|
||||
dao.update(
|
||||
DLCContactMappingDb(
|
||||
dlcDb1.dlcId,
|
||||
contactDb2.address
|
||||
)))
|
||||
|
||||
_ <- daos.contactDAO.create(contactDb2)
|
||||
|
||||
updated <- dao.update(
|
||||
DLCContactMappingDb(
|
||||
dlcDb1.dlcId,
|
||||
contactDb2.address
|
||||
))
|
||||
read <- dao.read(dlcDb1.dlcId)
|
||||
} yield {
|
||||
assert(read.isDefined)
|
||||
assert(read.get == updated)
|
||||
assert(updated.dlcId == dlcDb1.dlcId)
|
||||
assert(updated.contactId == contactDb2.address)
|
||||
}
|
||||
}
|
||||
|
||||
it should "upsert rows and enforce foreign keys" in { daos =>
|
||||
val dao = daos.dlcContactMappingDAO
|
||||
|
||||
for {
|
||||
// no such DLC
|
||||
_ <- recoverToSucceededIf[SQLException](
|
||||
dao.upsert(
|
||||
DLCContactMappingDb(
|
||||
dlcDb1.dlcId,
|
||||
contactDb1.address
|
||||
)))
|
||||
|
||||
_ <- daos.dlcDAO.create(dlcDb1)
|
||||
|
||||
// no such contact
|
||||
_ <- recoverToSucceededIf[SQLException](
|
||||
dao.upsert(
|
||||
DLCContactMappingDb(
|
||||
dlcDb1.dlcId,
|
||||
contactDb1.address
|
||||
)))
|
||||
|
||||
_ <- daos.contactDAO.create(contactDb1)
|
||||
|
||||
// both DLC and contact exist
|
||||
upserted1 <- dao.upsert(
|
||||
DLCContactMappingDb(
|
||||
dlcDb1.dlcId,
|
||||
contactDb1.address
|
||||
))
|
||||
read1 <- dao.read(dlcDb1.dlcId)
|
||||
|
||||
_ <- daos.contactDAO.create(contactDb2)
|
||||
|
||||
upserted2 <- dao.upsert(
|
||||
DLCContactMappingDb(
|
||||
dlcDb1.dlcId,
|
||||
contactDb2.address
|
||||
))
|
||||
read2 <- dao.read(dlcDb1.dlcId)
|
||||
} yield {
|
||||
assert(read1.isDefined)
|
||||
assert(read1.get == upserted1)
|
||||
assert(upserted1.dlcId == dlcDb1.dlcId)
|
||||
assert(upserted1.contactId == contactDb1.address)
|
||||
|
||||
assert(read2.isDefined)
|
||||
assert(read2.get == upserted2)
|
||||
assert(upserted2.dlcId == dlcDb1.dlcId)
|
||||
assert(upserted2.contactId == contactDb2.address)
|
||||
}
|
||||
}
|
||||
|
||||
it should "list all DLCs with and without associated contacts" in { daos =>
|
||||
for {
|
||||
_ <- daos.dlcDAO.create(dlcDb1)
|
||||
_ <- daos.dlcDAO.create(dlcDb2)
|
||||
_ <- daos.contactDAO.create(contactDb1)
|
||||
_ <- daos.dlcContactMappingDAO.create(dlcDb1, contactDb1)
|
||||
actual <- daos.dlcContactMappingDAO.listAll()
|
||||
} yield {
|
||||
assert(actual.size == 2)
|
||||
val expected = Vector(
|
||||
DLCContactMapping(dlcDb1, Some(contactDb1)),
|
||||
DLCContactMapping(dlcDb2, None)
|
||||
).sortBy(_.dlc.dlcId.hex)
|
||||
assert(actual.sortBy(_.dlc.dlcId.hex) == expected)
|
||||
}
|
||||
}
|
||||
|
||||
it should "find all DLCs by contact" in { daos =>
|
||||
for {
|
||||
_ <- daos.dlcDAO.create(dlcDb1)
|
||||
_ <- daos.dlcDAO.create(dlcDb2)
|
||||
_ <- daos.contactDAO.create(contactDb1)
|
||||
_ <- daos.dlcContactMappingDAO.create(dlcDb1, contactDb1)
|
||||
actual1 <- daos.dlcContactMappingDAO.findDLCsByContactId(contactId =
|
||||
contactDb1.address)
|
||||
actual2 <- daos.dlcContactMappingDAO.findDLCsByContactId(contactId =
|
||||
contactDb2.address)
|
||||
} yield {
|
||||
assert(actual1.size == 1)
|
||||
assert(actual2.isEmpty)
|
||||
val expected1 = Vector(dlcDb1)
|
||||
assert(actual1 == expected1)
|
||||
}
|
||||
}
|
||||
|
||||
it should "find contact by DLCs" in { daos =>
|
||||
for {
|
||||
_ <- daos.dlcDAO.create(dlcDb1)
|
||||
_ <- daos.dlcDAO.create(dlcDb2)
|
||||
_ <- daos.contactDAO.create(contactDb1)
|
||||
_ <- daos.dlcContactMappingDAO.create(dlcDb1, contactDb1)
|
||||
actual1 <- daos.dlcContactMappingDAO.findContactByDLCId(dlcDb1.dlcId)
|
||||
actual2 <- daos.dlcContactMappingDAO.findContactByDLCId(dlcDb2.dlcId)
|
||||
} yield {
|
||||
assert(actual1.size == 1)
|
||||
assert(actual2.isEmpty)
|
||||
val expected1 = Some(contactDb1)
|
||||
assert(actual1 == expected1)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
CREATE TABLE "dlc_contact_mapping" (
|
||||
"dlc_id" TEXT NOT NULL PRIMARY KEY,
|
||||
"contact_id" TEXT NOT NULL,
|
||||
CONSTRAINT "fk_dlc_contact_dlc_id" FOREIGN KEY ("dlc_id") references "global_dlc_data" ("dlc_id") on update NO ACTION on delete NO ACTION,
|
||||
CONSTRAINT "fk_dlc_contact_contact_id" FOREIGN KEY ("contact_id") references "contacts" ("address") on update NO ACTION on delete NO ACTION
|
||||
);
|
|
@ -0,0 +1,6 @@
|
|||
CREATE TABLE "dlc_contact_mapping" (
|
||||
"dlc_id" TEXT NOT NULL PRIMARY KEY,
|
||||
"contact_id" TEXT NOT NULL,
|
||||
CONSTRAINT "fk_dlc_contact_dlc_id" FOREIGN KEY ("dlc_id") references "global_dlc_data" ("dlc_id") on update NO ACTION on delete NO ACTION,
|
||||
CONSTRAINT "fk_dlc_contact_contact_id" FOREIGN KEY ("contact_id") references "contacts" ("address") on update NO ACTION on delete NO ACTION
|
||||
);
|
|
@ -2,7 +2,7 @@ package org.bitcoins.dlc.wallet
|
|||
|
||||
import org.bitcoins.core.api.chain.ChainQueryApi
|
||||
import org.bitcoins.core.api.dlc.wallet.AnyDLCHDWalletApi
|
||||
import org.bitcoins.core.api.dlc.wallet.db.DLCDb
|
||||
import org.bitcoins.core.api.dlc.wallet.db.{DLCContactDb, DLCDb}
|
||||
import org.bitcoins.core.api.feeprovider.FeeRateApi
|
||||
import org.bitcoins.core.api.node.NodeApi
|
||||
import org.bitcoins.core.api.wallet.db._
|
||||
|
@ -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 */
|
||||
|
@ -77,6 +78,9 @@ abstract class DLCWallet
|
|||
private[bitcoins] val contactDAO: DLCContactDAO =
|
||||
DLCContactDAO()
|
||||
|
||||
private[bitcoins] val dlcContactMappingDAO: DLCContactMappingDAO =
|
||||
DLCContactMappingDAO()
|
||||
|
||||
private[wallet] val dlcWalletDAOs = DLCWalletDAOs(
|
||||
dlcDAO,
|
||||
contractDataDAO,
|
||||
|
@ -805,7 +809,8 @@ abstract class DLCWallet
|
|||
offerDb = initializedAccept.offerDb,
|
||||
payoutAddress = Some(
|
||||
PayoutAddress(initializedAccept.pubKeys.payoutAddress,
|
||||
isExternalAddress))
|
||||
isExternalAddress)),
|
||||
contactOpt = None
|
||||
)
|
||||
_ = dlcConfig.walletCallbacks.executeOnDLCStateChange(logger, status)
|
||||
cetSigs <- signer.createCETSigsAsync()
|
||||
|
@ -1630,6 +1635,7 @@ abstract class DLCWallet
|
|||
for {
|
||||
dlcDbOpt <- dlcDAO.findByContractId(contractId)
|
||||
dlcDb = dlcDbOpt.get
|
||||
contactOpt <- dlcContactMappingDAO.findContactByDLCId(dlcDb.dlcId)
|
||||
offerDbOpt <- dlcOfferDAO.findByDLCId(dlcDb.dlcId)
|
||||
_ = require(offerDbOpt.nonEmpty,
|
||||
s"Invalid DLC $dlcDb.dlcId: no offer data")
|
||||
|
@ -1678,7 +1684,8 @@ abstract class DLCWallet
|
|||
contractData,
|
||||
offerDbOpt.get,
|
||||
dlcAcceptOpt,
|
||||
closingTxOpt)
|
||||
closingTxOpt,
|
||||
contactOpt)
|
||||
_ <- dlcConfig.walletCallbacks.executeOnDLCStateChange(logger, status.get)
|
||||
} yield refundTx
|
||||
}
|
||||
|
@ -1696,8 +1703,23 @@ 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) =>
|
||||
dlcContactMappingDAO.findDLCsByContactId(contactId)
|
||||
case None => dlcDAO.findAll()
|
||||
}
|
||||
ids = dlcs.map(_.dlcId)
|
||||
dlcFs = ids.map(findDLC)
|
||||
dlcs <- Future.sequence(dlcFs)
|
||||
} yield {
|
||||
|
@ -1760,6 +1782,7 @@ abstract class DLCWallet
|
|||
val contractDataOptF = contractDataDAO.read(dlcId)
|
||||
val offerDbOptF = dlcOfferDAO.read(dlcId)
|
||||
val acceptDbOptF = dlcAcceptDAO.read(dlcId)
|
||||
val contactDbOptF = dlcContactMappingDAO.findContactByDLCId(dlcId)
|
||||
val closingTxOptF: Future[Option[TransactionDb]] = getClosingTxOpt(dlcDb)
|
||||
|
||||
val dlcOptF: Future[Option[DLCStatus]] = for {
|
||||
|
@ -1767,6 +1790,7 @@ abstract class DLCWallet
|
|||
offerDbOpt <- offerDbOptF
|
||||
acceptDbOpt <- acceptDbOptF
|
||||
closingTxOpt <- closingTxOptF
|
||||
contactDbOpt <- contactDbOptF
|
||||
result <- {
|
||||
(contractDataOpt, offerDbOpt) match {
|
||||
case (Some(contractData), Some(offerDb)) =>
|
||||
|
@ -1774,7 +1798,8 @@ abstract class DLCWallet
|
|||
contractData,
|
||||
offerDb,
|
||||
acceptDbOpt,
|
||||
closingTxOpt)
|
||||
closingTxOpt,
|
||||
contactDbOpt)
|
||||
case (_, _) => Future.successful(None)
|
||||
}
|
||||
}
|
||||
|
@ -1788,7 +1813,8 @@ abstract class DLCWallet
|
|||
contractData: DLCContractDataDb,
|
||||
offerDb: DLCOfferDb,
|
||||
acceptDbOpt: Option[DLCAcceptDb],
|
||||
closingTxOpt: Option[TransactionDb]): Future[Option[DLCStatus]] = {
|
||||
closingTxOpt: Option[TransactionDb],
|
||||
contactOpt: Option[DLCContactDb]): Future[Option[DLCStatus]] = {
|
||||
val dlcId = dlcDb.dlcId
|
||||
val aggregatedF: Future[(
|
||||
Vector[DLCAnnouncementDb],
|
||||
|
@ -1823,7 +1849,8 @@ abstract class DLCWallet
|
|||
contractInfo = contractInfo,
|
||||
contractData = contractData,
|
||||
offerDb = offerDb,
|
||||
payoutAddress = payoutAddress)
|
||||
payoutAddress = payoutAddress,
|
||||
contactOpt = contactOpt)
|
||||
Future.successful(inProgress)
|
||||
case _: DLCState.ClosedState =>
|
||||
(acceptDbOpt, closingTxOpt) match {
|
||||
|
@ -1838,7 +1865,8 @@ abstract class DLCWallet
|
|||
offerDb = offerDb,
|
||||
acceptDb = acceptDb,
|
||||
closingTx = closingTx.transaction,
|
||||
payoutAddress = payoutAddress
|
||||
payoutAddress = payoutAddress,
|
||||
contactOpt = contactOpt
|
||||
)
|
||||
Future.successful(status)
|
||||
case (None, None) =>
|
||||
|
|
|
@ -58,4 +58,15 @@ trait IncomingDLCOffersHandling { self: DLCWallet =>
|
|||
override def findDLCContacts(alias: String): Future[Vector[DLCContactDb]] =
|
||||
contactDAO.findByAlias(alias)
|
||||
|
||||
override def addDLCContactMapping(
|
||||
dlcId: Sha256Digest,
|
||||
contcatId: InetSocketAddress): Future[Unit] = {
|
||||
dlcContactMappingDAO
|
||||
.create(dlcId, contcatId)
|
||||
.map(_ => ())
|
||||
}
|
||||
|
||||
override def removeDLCContactMapping(dlcId: Sha256Digest): Future[Unit] = {
|
||||
dlcContactMappingDAO.delete(dlcId)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
package org.bitcoins.dlc.wallet.models
|
||||
|
||||
import org.bitcoins.core.api.dlc.wallet.db.{DLCContactDb, DLCDb}
|
||||
import org.bitcoins.crypto.Sha256Digest
|
||||
import org.bitcoins.db.{CRUD, SlickUtil}
|
||||
import org.bitcoins.dlc.wallet.DLCAppConfig
|
||||
import slick.lifted.ForeignKeyQuery
|
||||
|
||||
import java.net.InetSocketAddress
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
case class DLCContactMapping(dlc: DLCDb, contact: Option[DLCContactDb]) {
|
||||
|
||||
def dlcId: Sha256Digest = dlc.dlcId
|
||||
|
||||
def toDLCContactMappingDb: Option[DLCContactMappingDb] =
|
||||
contact.map(c => DLCContactMappingDb(dlc.dlcId, c.address))
|
||||
}
|
||||
|
||||
case class DLCContactMappingDb(
|
||||
dlcId: Sha256Digest,
|
||||
contactId: InetSocketAddress)
|
||||
|
||||
case class DLCContactMappingDAO()(implicit
|
||||
override val ec: ExecutionContext,
|
||||
override val appConfig: DLCAppConfig)
|
||||
extends CRUD[DLCContactMappingDb, Sha256Digest]
|
||||
with SlickUtil[DLCContactMappingDb, Sha256Digest] {
|
||||
private val mappers = new org.bitcoins.db.DbCommonsColumnMappers(profile)
|
||||
import mappers._
|
||||
|
||||
import profile.api._
|
||||
|
||||
/** The table inside our database we are inserting into */
|
||||
override val table: TableQuery[DLCContactMappingTable] =
|
||||
TableQuery[DLCContactMappingTable]
|
||||
|
||||
private lazy val dlcTable: slick.lifted.TableQuery[DLCDAO#DLCTable] = {
|
||||
DLCDAO().table
|
||||
}
|
||||
|
||||
private lazy val contactTable: slick.lifted.TableQuery[
|
||||
DLCContactDAO#DLCContactTable] = {
|
||||
DLCContactDAO().table
|
||||
}
|
||||
|
||||
override def createAll(
|
||||
ts: Vector[DLCContactMappingDb]): Future[Vector[DLCContactMappingDb]] = {
|
||||
createAllNoAutoInc(ts, safeDatabase)
|
||||
}
|
||||
|
||||
def create(
|
||||
dlcDb: DLCDb,
|
||||
contactDb: DLCContactDb): Future[DLCContactMappingDb] = {
|
||||
create(DLCContactMappingDb(dlcDb.dlcId, contactDb.address))
|
||||
}
|
||||
|
||||
def create(
|
||||
dlcId: Sha256Digest,
|
||||
contactId: InetSocketAddress): Future[DLCContactMappingDb] = {
|
||||
create(DLCContactMappingDb(dlcId, contactId))
|
||||
}
|
||||
|
||||
def delete(dlcId: Sha256Digest): Future[Unit] = {
|
||||
safeDatabase.run(table.filter(_.dlcId === dlcId).delete).map(_ => ())
|
||||
}
|
||||
|
||||
def findContactByDLCId(dlcId: Sha256Digest): Future[Option[DLCContactDb]] = {
|
||||
val join =
|
||||
table.join(contactTable).on(_.contactId === _.address)
|
||||
safeDatabase
|
||||
.run(join.filter(_._1.dlcId === dlcId).result)
|
||||
.map(_.headOption.map(_._2))
|
||||
}
|
||||
|
||||
def findDLCsByContactId(
|
||||
contactId: InetSocketAddress): Future[Vector[DLCDb]] = {
|
||||
val join = table.join(dlcTable).on(_.dlcId === _.dlcId)
|
||||
safeDatabase
|
||||
.runVec(join.filter(_._1.contactId === contactId).result)
|
||||
.map(_.map(_._2))
|
||||
}
|
||||
|
||||
def listAll(): Future[Vector[DLCContactMapping]] = {
|
||||
val contactJoin = table.join(contactTable).on(_.contactId === _.address)
|
||||
val dlcJoin = dlcTable.joinLeft(contactJoin).on(_.dlcId === _._1.dlcId)
|
||||
safeDatabase
|
||||
.runVec(dlcJoin.result)
|
||||
.map(_.map(row => DLCContactMapping(row._1, row._2.map(_._2))))
|
||||
}
|
||||
|
||||
override protected def findByPrimaryKeys(ids: Vector[Sha256Digest]): Query[
|
||||
Table[DLCContactMappingDb],
|
||||
DLCContactMappingDb,
|
||||
Seq] = {
|
||||
table.filter(_.dlcId.inSet(ids))
|
||||
}
|
||||
|
||||
override protected def findAll(ts: Vector[DLCContactMappingDb]): Query[
|
||||
Table[_],
|
||||
DLCContactMappingDb,
|
||||
Seq] = findByPrimaryKeys(ts.map(_.dlcId))
|
||||
|
||||
class DLCContactMappingTable(tag: Tag)
|
||||
extends Table[DLCContactMappingDb](tag,
|
||||
schemaName,
|
||||
"dlc_contact_mapping") {
|
||||
|
||||
import profile.api._
|
||||
|
||||
def dlcId: Rep[Sha256Digest] = column("dlc_id", O.PrimaryKey)
|
||||
|
||||
def contactId: Rep[InetSocketAddress] = column("contact_id")
|
||||
|
||||
def fkDLCId: ForeignKeyQuery[_, DLCDb] =
|
||||
foreignKey("fk_dlc_contact_dlc_id",
|
||||
sourceColumns = dlcId,
|
||||
targetTableQuery = dlcTable)(_.dlcId)
|
||||
|
||||
def fkContactId: ForeignKeyQuery[_, DLCContactDb] =
|
||||
foreignKey("fk_dlc_contact_contact_id",
|
||||
sourceColumns = contactId,
|
||||
targetTableQuery = contactTable)(_.address)
|
||||
|
||||
def * =
|
||||
(dlcId,
|
||||
contactId) <> (DLCContactMappingDb.tupled, DLCContactMappingDb.unapply)
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package org.bitcoins.dlc.wallet.util
|
||||
|
||||
import org.bitcoins.core.api.dlc.wallet.db.DLCDb
|
||||
import org.bitcoins.core.api.dlc.wallet.db.{DLCContactDb, DLCDb}
|
||||
import org.bitcoins.core.dlc.accounting.DLCAccounting
|
||||
import org.bitcoins.core.protocol.dlc.models.DLCStatus._
|
||||
import org.bitcoins.core.protocol.dlc.models._
|
||||
|
@ -18,7 +18,8 @@ object DLCStatusBuilder {
|
|||
contractInfo: ContractInfo,
|
||||
contractData: DLCContractDataDb,
|
||||
offerDb: DLCOfferDb,
|
||||
payoutAddress: Option[PayoutAddress]): DLCStatus = {
|
||||
payoutAddress: Option[PayoutAddress],
|
||||
contactOpt: Option[DLCContactDb]): DLCStatus = {
|
||||
require(
|
||||
dlcDb.state.isInstanceOf[DLCState.InProgressState],
|
||||
s"Cannot have divergent states beteween dlcDb and the parameter state, got= dlcDb.state=${dlcDb.state} state=${dlcDb.state}"
|
||||
|
@ -45,7 +46,8 @@ object DLCStatusBuilder {
|
|||
dlcDb.feeRate,
|
||||
totalCollateral,
|
||||
localCollateral,
|
||||
payoutAddress
|
||||
payoutAddress,
|
||||
contactOpt
|
||||
)
|
||||
case DLCState.AcceptComputingAdaptorSigs =>
|
||||
AcceptedComputingAdaptorSigs(
|
||||
|
@ -59,7 +61,8 @@ object DLCStatusBuilder {
|
|||
feeRate = dlcDb.feeRate,
|
||||
totalCollateral = totalCollateral,
|
||||
localCollateral = localCollateral,
|
||||
payoutAddress
|
||||
payoutAddress,
|
||||
contactOpt
|
||||
)
|
||||
case DLCState.Accepted =>
|
||||
Accepted(
|
||||
|
@ -73,7 +76,8 @@ object DLCStatusBuilder {
|
|||
dlcDb.feeRate,
|
||||
totalCollateral,
|
||||
localCollateral,
|
||||
payoutAddress
|
||||
payoutAddress,
|
||||
contactOpt
|
||||
)
|
||||
case DLCState.SignComputingAdaptorSigs =>
|
||||
SignedComputingAdaptorSigs(
|
||||
|
@ -88,7 +92,8 @@ object DLCStatusBuilder {
|
|||
totalCollateral = totalCollateral,
|
||||
localCollateral = localCollateral,
|
||||
dlcDb.fundingTxIdOpt.get,
|
||||
payoutAddress
|
||||
payoutAddress,
|
||||
contactOpt
|
||||
)
|
||||
case DLCState.Signed =>
|
||||
Signed(
|
||||
|
@ -103,7 +108,8 @@ object DLCStatusBuilder {
|
|||
totalCollateral,
|
||||
localCollateral,
|
||||
dlcDb.fundingTxIdOpt.get,
|
||||
payoutAddress
|
||||
payoutAddress,
|
||||
contactOpt
|
||||
)
|
||||
case DLCState.Broadcasted =>
|
||||
Broadcasted(
|
||||
|
@ -118,7 +124,8 @@ object DLCStatusBuilder {
|
|||
totalCollateral,
|
||||
localCollateral,
|
||||
dlcDb.fundingTxIdOpt.get,
|
||||
payoutAddress
|
||||
payoutAddress,
|
||||
contactOpt
|
||||
)
|
||||
case DLCState.Confirmed =>
|
||||
Confirmed(
|
||||
|
@ -133,7 +140,8 @@ object DLCStatusBuilder {
|
|||
totalCollateral,
|
||||
localCollateral,
|
||||
dlcDb.fundingTxIdOpt.get,
|
||||
payoutAddress
|
||||
payoutAddress,
|
||||
contactOpt
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -150,7 +158,8 @@ object DLCStatusBuilder {
|
|||
offerDb: DLCOfferDb,
|
||||
acceptDb: DLCAcceptDb,
|
||||
closingTx: Transaction,
|
||||
payoutAddress: Option[PayoutAddress]): ClosedDLCStatus = {
|
||||
payoutAddress: Option[PayoutAddress],
|
||||
contactOpt: Option[DLCContactDb]): ClosedDLCStatus = {
|
||||
require(
|
||||
dlcDb.state.isInstanceOf[DLCState.ClosedState],
|
||||
s"Cannot have divergent states beteween dlcDb and the parameter state, got= dlcDb.state=${dlcDb.state} state=${dlcDb.state}"
|
||||
|
@ -186,7 +195,8 @@ object DLCStatusBuilder {
|
|||
closingTx.txIdBE,
|
||||
myPayout = accounting.myPayout,
|
||||
counterPartyPayout = accounting.theirPayout,
|
||||
payoutAddress = payoutAddress
|
||||
payoutAddress = payoutAddress,
|
||||
contact = contactOpt
|
||||
)
|
||||
refund
|
||||
case oracleOutcomeState: DLCState.ClosedViaOracleOutcomeState =>
|
||||
|
@ -214,7 +224,8 @@ object DLCStatusBuilder {
|
|||
oracleOutcome,
|
||||
myPayout = accounting.myPayout,
|
||||
counterPartyPayout = accounting.theirPayout,
|
||||
payoutAddress = payoutAddress
|
||||
payoutAddress = payoutAddress,
|
||||
contact = contactOpt
|
||||
)
|
||||
case DLCState.RemoteClaimed =>
|
||||
RemoteClaimed(
|
||||
|
@ -234,7 +245,8 @@ object DLCStatusBuilder {
|
|||
oracleOutcome,
|
||||
myPayout = accounting.myPayout,
|
||||
counterPartyPayout = accounting.theirPayout,
|
||||
payoutAddress = payoutAddress
|
||||
payoutAddress = payoutAddress,
|
||||
contact = contactOpt
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -278,6 +278,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
|
||||
|
|
|
@ -21,7 +21,9 @@ case class DLCDAOs(
|
|||
dlcSigsDAO: DLCCETSignaturesDAO,
|
||||
dlcRefundSigDAO: DLCRefundSigsDAO,
|
||||
dlcRemoteTxDAO: DLCRemoteTxDAO,
|
||||
incomingDLCOfferDAO: IncomingDLCOfferDAO) {
|
||||
incomingDLCOfferDAO: IncomingDLCOfferDAO,
|
||||
contactDAO: DLCContactDAO,
|
||||
dlcContactMappingDAO: DLCContactMappingDAO) {
|
||||
|
||||
val list = Vector(
|
||||
announcementDAO,
|
||||
|
@ -35,7 +37,9 @@ case class DLCDAOs(
|
|||
dlcSigsDAO,
|
||||
dlcRefundSigDAO,
|
||||
dlcRemoteTxDAO,
|
||||
incomingDLCOfferDAO
|
||||
incomingDLCOfferDAO,
|
||||
contactDAO: DLCContactDAO,
|
||||
dlcContactMappingDAO: DLCContactMappingDAO
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -54,6 +58,8 @@ trait DLCDAOFixture extends BitcoinSFixture with EmbeddedPg {
|
|||
val dlcRefundSigDAO = DLCRefundSigsDAO()
|
||||
val dlcRemoteTxDAO = DLCRemoteTxDAO()
|
||||
val incomingDLCOfferDAO = IncomingDLCOfferDAO()
|
||||
val contactDAO = DLCContactDAO()
|
||||
val dlcContactMappingDAO = DLCContactMappingDAO()
|
||||
DLCDAOs(
|
||||
announcementDAO = announcementDAO,
|
||||
nonceDAO = nonceDAO,
|
||||
|
@ -66,7 +72,9 @@ trait DLCDAOFixture extends BitcoinSFixture with EmbeddedPg {
|
|||
dlcSigsDAO = dlcSigsDAO,
|
||||
dlcRefundSigDAO = dlcRefundSigDAO,
|
||||
dlcRemoteTxDAO = dlcRemoteTxDAO,
|
||||
incomingDLCOfferDAO = incomingDLCOfferDAO
|
||||
incomingDLCOfferDAO = incomingDLCOfferDAO,
|
||||
contactDAO = contactDAO,
|
||||
dlcContactMappingDAO = dlcContactMappingDAO
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue