2022 01 14 DLCDataManagement refactor (#3982)

* WIP

* Finish refactor, get unit tests working

* Remove log
This commit is contained in:
Chris Stewart 2022-01-18 08:12:27 -06:00 committed by GitHub
parent 214213b59d
commit ee0d62c5b8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 415 additions and 227 deletions

View file

@ -100,7 +100,6 @@ case class ChainAppConfig(
logger.info(s"Applied ${numMigrations} to chain project")
()
}
}
override def stop(): Future[Unit] = {

View file

@ -1,5 +1,7 @@
package org.bitcoins.core.protocol.tlv
import org.bitcoins.crypto.StringFactory
/** We have various binary serializations in our codebase currently.
* This is a product of trying to release a DLC wallet before the
* spec was finalized. Some of the binary level serialization for DLCs
@ -7,7 +9,7 @@ package org.bitcoins.core.protocol.tlv
*/
sealed trait DLCSerializationVersion
object DLCSerializationVersion {
object DLCSerializationVersion extends StringFactory[DLCSerializationVersion] {
/** This format existed in our wallet before we merged support for this PR
* on the DLC spec repo. See the diff below
@ -20,4 +22,19 @@ object DLCSerializationVersion {
* @see [[https://github.com/discreetlogcontracts/dlcspecs/pull/144]]
*/
case object Beta extends DLCSerializationVersion
private val all = Vector(Alpha, Beta)
override def fromString(str: String): DLCSerializationVersion = {
fromStringOpt(str) match {
case Some(version) => version
case None =>
sys.error(
s"Could not find DLC serialization version associated with str=$str")
}
}
override def fromStringOpt(str: String): Option[DLCSerializationVersion] = {
all.find(_.toString.toLowerCase == str.toLowerCase)
}
}

View file

@ -489,4 +489,11 @@ class DbCommonsColumnMappers(val profile: JdbcProfile) {
MappedColumnType.base[ExtKeyPubVersion, String](_.hex,
ExtKeyPubVersion.fromHex)
}
implicit val dlcSerializationVersion: BaseColumnType[
DLCSerializationVersion] = {
MappedColumnType.base[DLCSerializationVersion, String](
_.toString,
DLCSerializationVersion.fromString)
}
}

View file

@ -9,6 +9,7 @@ import org.bitcoins.core.protocol.tlv._
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
import org.bitcoins.core.wallet.utxo.TxoState
import org.bitcoins.crypto._
import org.bitcoins.dlc.wallet.internal.DLCDataManagement
import org.bitcoins.testkit.wallet.DLCWalletUtil._
import org.bitcoins.testkit.wallet.FundWalletUtil.FundedDLCWallet
import org.bitcoins.testkit.wallet.{BitcoinSDualWalletTest, DLCWalletUtil}
@ -31,7 +32,8 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
offerData: DLCOffer): Future[Assertion] = {
val walletA = fundedDLCWallets._1.wallet
val walletB = fundedDLCWallets._2.wallet
val walletADLCManagement = DLCDataManagement(walletA.dlcWalletDAOs)
val walletBDLCManagement = DLCDataManagement(walletB.dlcWalletDAOs)
for {
offer <- walletA.createDLCOffer(
offerData.contractInfo,
@ -91,17 +93,19 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
walletBChange <- walletB.addressDAO.read(accept.changeAddress)
walletBFinal <- walletB.addressDAO.read(accept.pubKeys.payoutAddress)
(announcementsA, announcementDataA, nonceDbsA) <- walletA
(announcementsA, announcementDataA, nonceDbsA) <- walletADLCManagement
.getDLCAnnouncementDbs(dlcDb.dlcId)
announcementTLVsA = walletA.getOracleAnnouncements(announcementsA,
announcementDataA,
nonceDbsA)
announcementTLVsA = walletADLCManagement.getOracleAnnouncements(
announcementsA,
announcementDataA,
nonceDbsA)
(announcementsB, announcementDataB, nonceDbsB) <- walletA
(announcementsB, announcementDataB, nonceDbsB) <- walletADLCManagement
.getDLCAnnouncementDbs(dlcDb.dlcId)
announcementTLVsB = walletB.getOracleAnnouncements(announcementsB,
announcementDataB,
nonceDbsB)
announcementTLVsB = walletBDLCManagement.getOracleAnnouncements(
announcementsB,
announcementDataB,
nonceDbsB)
} yield {
assert(dlcDb.contractIdOpt.get == sign.contractId)

View file

@ -29,7 +29,11 @@ import org.bitcoins.crypto._
import org.bitcoins.db.SafeDatabase
import org.bitcoins.dlc.wallet.internal._
import org.bitcoins.dlc.wallet.models._
import org.bitcoins.dlc.wallet.util.{DLCActionBuilder, DLCStatusBuilder}
import org.bitcoins.dlc.wallet.util.{
DLCActionBuilder,
DLCStatusBuilder,
DLCTxUtil
}
import org.bitcoins.wallet.config.WalletAppConfig
import org.bitcoins.wallet.{Wallet, WalletLogger}
import scodec.bits.ByteVector
@ -41,7 +45,6 @@ import scala.concurrent.{ExecutionContext, Future}
abstract class DLCWallet
extends Wallet
with AnyDLCHDWalletApi
with DLCDataManagement
with DLCTransactionProcessing {
implicit val dlcConfig: DLCAppConfig
@ -63,18 +66,23 @@ abstract class DLCWallet
private[bitcoins] val dlcRefundSigDAO: DLCRefundSigsDAO = DLCRefundSigsDAO()
private[bitcoins] val remoteTxDAO: DLCRemoteTxDAO = DLCRemoteTxDAO()
private[wallet] val dlcWalletDAOs = DLCWalletDAOs(
dlcDAO,
contractDataDAO,
dlcAnnouncementDAO,
dlcInputsDAO,
dlcOfferDAO,
dlcAcceptDAO,
dlcSigsDAO,
dlcRefundSigDAO,
oracleNonceDAO,
announcementDAO
)
private val dlcDataManagement = DLCDataManagement(dlcWalletDAOs)
protected lazy val actionBuilder: DLCActionBuilder = {
DLCActionBuilder(
dlcDAO = dlcDAO,
contractDataDAO = contractDataDAO,
dlcAnnouncementDAO = dlcAnnouncementDAO,
dlcInputsDAO = dlcInputsDAO,
dlcOfferDAO = dlcOfferDAO,
dlcAcceptDAO = dlcAcceptDAO,
dlcSigsDAO = dlcSigsDAO,
dlcRefundSigDAO = dlcRefundSigDAO,
oracleNonceDAO = oracleNonceDAO
)
DLCActionBuilder(dlcWalletDAOs)
}
private lazy val safeDatabase: SafeDatabase = dlcDAO.safeDatabase
@ -578,7 +586,8 @@ abstract class DLCWallet
outcomeSigsDbs <- dlcSigsDAO.findByDLCId(dlcId)
refundSigsDb <- dlcRefundSigDAO.read(dlcId)
} yield {
val inputRefs = matchPrevTxsWithInputs(fundingInputs, prevTxs)
val inputRefs =
DLCTxUtil.matchPrevTxsWithInputs(fundingInputs, prevTxs)
dlcAcceptDb.toDLCAccept(offer.tempContractId,
inputRefs,
@ -850,19 +859,20 @@ abstract class DLCWallet
transactionDAO.findByTxIdBEs(offerInputs.map(_.outPoint.txIdBE))
contractData <- contractDataDAO.read(dlcId).map(_.get)
(announcements, announcementData, nonceDbs) <- getDLCAnnouncementDbs(
dlcId)
(announcements, announcementData, nonceDbs) <- dlcDataManagement
.getDLCAnnouncementDbs(dlcId)
contractInfo = getContractInfo(contractData,
announcements,
announcementData,
nonceDbs)
contractInfo = dlcDataManagement.getContractInfo(contractData,
announcements,
announcementData,
nonceDbs)
offer =
offerDb.toDLCOffer(contractInfo,
matchPrevTxsWithInputs(offerInputs, prevTxs),
dlc,
contractData)
offerDb.toDLCOffer(
contractInfo,
DLCTxUtil.matchPrevTxsWithInputs(offerInputs, prevTxs),
dlc,
contractData)
dlcDb <- updateDLCContractIds(offer, accept)
@ -904,7 +914,7 @@ abstract class DLCWallet
Future.failed(new RuntimeException(
s"No DLC found with corresponding tempContractId ${tempId.hex}, this wallet did not create the corresponding offer"))
}
contractInfo <- getContractInfo(dlcDb.dlcId)
contractInfo <- dlcDataManagement.getContractInfo(dlcDb.dlcId)
accept =
DLCAccept.fromTLV(acceptTLV, walletConfig.network, contractInfo)
@ -922,7 +932,14 @@ abstract class DLCWallet
// .get should be safe now
contractId = dlc.contractIdOpt.get
dlcId = dlc.dlcId
signer <- signerFromDb(dlc.dlcId)
fundingInputs <- dlcInputsDAO.findByDLCId(dlcId)
scriptSigParams <- getScriptSigParams(dlc, fundingInputs)
signer <- dlcDataManagement.signerFromDb(dlcId = dlc.dlcId,
transactionDAO = transactionDAO,
remoteTxDAO = remoteTxDAO,
fundingUtxoScriptSigParams =
scriptSigParams,
keyManager = keyManager)
mySigs <- dlcSigsDAO.findByDLCId(dlc.dlcId)
refundSigsDb <- dlcRefundSigDAO.findByDLCId(dlc.dlcId).map(_.head)
@ -977,29 +994,42 @@ abstract class DLCWallet
}
def verifyCETSigs(accept: DLCAccept): Future[Boolean] = {
verifierFromAccept(accept).flatMap(
_.verifyCETSigs(accept.cetSigs.indexedOutcomeSigs))
val verifierAcceptF =
dlcDataManagement.verifierFromAccept(accept, transactionDAO)
verifierAcceptF.flatMap(_.verifyCETSigs(accept.cetSigs.indexedOutcomeSigs))
}
def verifyCETSigs(sign: DLCSign): Future[Boolean] = {
verifierFromDb(sign.contractId).flatMap(
_.verifyCETSigs(sign.cetSigs.indexedOutcomeSigs))
val verifierF = dlcDataManagement.verifierFromDb(sign.contractId,
transactionDAO,
remoteTxDAO)
verifierF.flatMap(_.verifyCETSigs(sign.cetSigs.indexedOutcomeSigs))
}
def verifyRefundSig(accept: DLCAccept): Future[Boolean] = {
verifierFromAccept(accept).map(_.verifyRefundSig(accept.cetSigs.refundSig))
val verifierF = dlcDataManagement.verifierFromAccept(accept, transactionDAO)
verifierF.map(_.verifyRefundSig(accept.cetSigs.refundSig))
}
def verifyRefundSig(sign: DLCSign): Future[Boolean] = {
verifierFromDb(sign.contractId).map(
_.verifyRefundSig(sign.cetSigs.refundSig))
val verifierF = dlcDataManagement.verifierFromDb(
contractId = sign.contractId,
transactionDAO = transactionDAO,
remoteTxDAO = remoteTxDAO)
verifierF.map(_.verifyRefundSig(sign.cetSigs.refundSig))
}
def verifyFundingSigs(
inputs: Vector[DLCFundingInputDb],
sign: DLCSign): Future[Boolean] = {
if (inputs.count(_.isInitiator) == sign.fundingSigs.length) {
verifierFromDb(sign.contractId).map { verifier =>
val verifierF = dlcDataManagement.verifierFromDb(
contractId = sign.contractId,
transactionDAO = transactionDAO,
remoteTxDAO = remoteTxDAO)
verifierF.map { verifier =>
verifier.verifyRemoteFundingSigs(sign.fundingSigs)
}
} else {
@ -1054,13 +1084,17 @@ abstract class DLCWallet
s"No DLC found with corresponding contractId ${contractId.toHex}"))
}
(_, contractData, dlcOffer, dlcAccept, fundingInputs, contractInfo) <-
getDLCFundingData(dlcDb.dlcId)
builder <- builderFromDbData(dlcDb,
contractData,
dlcOffer,
dlcAccept,
fundingInputs,
contractInfo)
dlcDataManagement.getDLCFundingData(dlcDb.dlcId)
builder <- dlcDataManagement.builderFromDbData(
dlcDb = dlcDb,
contractDataDb = contractData,
dlcOffer = dlcOffer,
dlcAccept = dlcAccept,
fundingInputsDb = fundingInputs,
contractInfo = contractInfo,
transactionDAO = transactionDAO,
remoteTxDAO = remoteTxDAO
)
sign = DLCSign.fromTLV(signTLV, builder.offer)
result <- addDLCSigs(sign)
@ -1127,17 +1161,44 @@ abstract class DLCWallet
}
}
private[wallet] def getScriptSigParams(
dlcDb: DLCDb,
fundingInputs: Vector[DLCFundingInputDb]): Future[
Vector[ScriptSignatureParams[InputInfo]]] = {
val outPoints =
fundingInputs.filter(_.isInitiator == dlcDb.isInitiator).map(_.outPoint)
val utxosF = listUtxos(outPoints)
for {
utxos <- utxosF
scriptSigParams <-
FutureUtil.foldLeftAsync(Vector.empty[ScriptSignatureParams[InputInfo]],
utxos) { (accum, utxo) =>
transactionDAO
.findByOutPoint(utxo.outPoint)
.map(txOpt =>
utxo.toUTXOInfo(keyManager, txOpt.get.transaction) +: accum)
}
} yield scriptSigParams
}
override def getDLCFundingTx(contractId: ByteVector): Future[Transaction] = {
for {
(dlcDb, contractData, dlcOffer, dlcAccept, fundingInputs, contractInfo) <-
getDLCFundingData(contractId)
dlcDataManagement.getDLCFundingData(contractId)
signer <- signerFromDb(dlcDb,
contractData,
dlcOffer,
dlcAccept,
fundingInputs,
contractInfo)
scriptSigParams <- getScriptSigParams(dlcDb, fundingInputs)
signer <- dlcDataManagement.signerFromDb(
dlcDb = dlcDb,
contractDataDb = contractData,
dlcOffer = dlcOffer,
dlcAccept = dlcAccept,
fundingInputsDb = fundingInputs,
fundingUtxoScriptSigParams = scriptSigParams,
contractInfo = contractInfo,
transactionDAO = transactionDAO,
remoteTxDAO = remoteTxDAO,
keyManager = keyManager
)
fundingTx <-
if (dlcDb.isInitiator) {
transactionDAO.findByTxId(signer.builder.buildFundingTx.txIdBE).map {
@ -1219,12 +1280,13 @@ abstract class DLCWallet
for {
dlcDb <- dlcDAO.findByContractId(contractId).map(_.get)
(announcements, announcementData, nonceDbs) <- getDLCAnnouncementDbs(
dlcDb.dlcId)
(announcements, announcementData, nonceDbs) <- dlcDataManagement
.getDLCAnnouncementDbs(dlcDb.dlcId)
announcementTLVs = getOracleAnnouncements(announcements,
announcementData,
nonceDbs)
announcementTLVs = dlcDataManagement.getOracleAnnouncements(
announcements,
announcementData,
nonceDbs)
oracleSigs =
sigs.foldLeft(Vector.empty[OracleSignatures]) { (acc, sig) =>
@ -1272,9 +1334,16 @@ abstract class DLCWallet
contractId: ByteVector,
oracleSigs: Vector[OracleSignatures]): Future[Transaction] = {
require(oracleSigs.nonEmpty, "Must provide at least one oracle signature")
val executorWithSetupOptF = executorAndSetupFromDb(contractId)
for {
executorWithSetupOpt <- executorWithSetupOptF
(dlcDb, _, _, _, fundingInputs, _) <-
dlcDataManagement.getDLCFundingData(contractId)
scriptSigParams <- getScriptSigParams(dlcDb, fundingInputs)
executorWithSetupOpt <- dlcDataManagement.executorAndSetupFromDb(
contractId = contractId,
transactionDAO = transactionDAO,
remoteTxDAO = remoteTxDAO,
fundingUtxoScriptSigParams = scriptSigParams,
keyManager = keyManager)
tx <- {
executorWithSetupOpt match {
case Some(executorWithSetup) =>
@ -1359,7 +1428,13 @@ abstract class DLCWallet
s"Refund transaction is not valid yet, current time: $currentTime, refund valid at time $time")
}
executor <- executorFromDb(dlcDb.dlcId)
fundingInputs <- dlcInputsDAO.findByDLCId(dlcDb.dlcId)
scriptSigParams <- getScriptSigParams(dlcDb, fundingInputs)
executor <- dlcDataManagement.executorFromDb(dlcDb.dlcId,
transactionDAO,
remoteTxDAO,
scriptSigParams,
keyManager)
refundSigsDbOpt <- dlcRefundSigDAO.findByDLCId(dlcDb.dlcId)
refundSig =
@ -1462,18 +1537,20 @@ abstract class DLCWallet
val aggregatedF: Future[(
Vector[DLCAnnouncementDb],
Vector[OracleAnnouncementDataDb],
Vector[OracleNonceDb])] = getDLCAnnouncementDbs(dlcDb.dlcId)
Vector[OracleNonceDb])] =
dlcDataManagement.getDLCAnnouncementDbs(dlcDb.dlcId)
val contractInfoAndAnnouncementsF: Future[
(ContractInfo, Vector[(OracleAnnouncementV0TLV, Long)])] = {
aggregatedF.map { case (announcements, announcementData, nonceDbs) =>
val contractInfo = getContractInfo(contractData,
announcements,
announcementData,
nonceDbs)
val announcementsWithId = getOracleAnnouncementsWithId(announcements,
announcementData,
nonceDbs)
val contractInfo = dlcDataManagement.getContractInfo(contractData,
announcements,
announcementData,
nonceDbs)
val announcementsWithId =
dlcDataManagement.getOracleAnnouncementsWithId(announcements,
announcementData,
nonceDbs)
(contractInfo, announcementsWithId)
}
}

View file

@ -1,7 +1,6 @@
package org.bitcoins.dlc.wallet.internal
import org.bitcoins.core.api.dlc.wallet.db.DLCDb
import org.bitcoins.core.api.wallet.db.TransactionDb
import org.bitcoins.core.hd._
import org.bitcoins.core.protocol.dlc.build.DLCTxBuilder
import org.bitcoins.core.protocol.dlc.execution._
@ -11,19 +10,35 @@ import org.bitcoins.core.protocol.dlc.sign.DLCTxSigner
import org.bitcoins.core.protocol.dlc.verify.DLCSignatureVerifier
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.tlv._
import org.bitcoins.core.util.FutureUtil
import org.bitcoins.core.util.sorted.{OrderedAnnouncements, OrderedNonces}
import org.bitcoins.core.wallet.utxo._
import org.bitcoins.crypto.Sha256Digest
import org.bitcoins.dlc.wallet.DLCWallet
import org.bitcoins.dlc.wallet.models._
import org.bitcoins.dlc.wallet.util.{DLCActionBuilder, DLCTxUtil}
import org.bitcoins.keymanager.bip39.BIP39KeyManager
import org.bitcoins.wallet.models.TransactionDAO
import scodec.bits._
import slick.dbio.{DBIOAction, Effect, NoStream}
import scala.concurrent._
/** Handles fetching and constructing different DLC datastructures from the database */
private[bitcoins] trait DLCDataManagement { self: DLCWallet =>
case class DLCDataManagement(dlcWalletDAOs: DLCWalletDAOs)(implicit
ec: ExecutionContext) {
private val dlcDAO = dlcWalletDAOs.dlcDAO
private val dlcAnnouncementDAO = dlcWalletDAOs.dlcAnnouncementDAO
//private val dlcInputsDAO = dlcWalletDAOs.dlcInputsDAO
// private val dlcOfferDAO = dlcWalletDAOs.dlcOfferDAO
private val contractDataDAO = dlcWalletDAOs.contractDataDAO
private val dlcAcceptDAO = dlcWalletDAOs.dlcAcceptDAO
private val dlcSigsDAO = dlcWalletDAOs.dlcSigsDAO
private val dlcRefundSigDAO = dlcWalletDAOs.dlcRefundSigDAO
private val announcementDAO = dlcWalletDAOs.oracleAnnouncementDAO
private val oracleNonceDAO = dlcWalletDAOs.oracleNonceDAO
private val actionBuilder: DLCActionBuilder = {
DLCActionBuilder(dlcWalletDAOs)
}
private lazy val safeDatabase = dlcDAO.safeDatabase
private[wallet] def getDLCAnnouncementDbs(dlcId: Sha256Digest): Future[(
@ -322,28 +337,9 @@ private[bitcoins] trait DLCDataManagement { self: DLCWallet =>
}
}
private[wallet] def fundingUtxosFromDb(
dlcDb: DLCDb,
fundingInputs: Vector[DLCFundingInputDb]): Future[
Vector[ScriptSignatureParams[InputInfo]]] = {
val outPoints =
fundingInputs.filter(_.isInitiator == dlcDb.isInitiator).map(_.outPoint)
for {
utxos <- listUtxos(outPoints)
scriptSigParams <-
FutureUtil.foldLeftAsync(Vector.empty[ScriptSignatureParams[InputInfo]],
utxos) { (accum, utxo) =>
transactionDAO
.findByOutPoint(utxo.outPoint)
.map(txOpt =>
utxo.toUTXOInfo(keyManager, txOpt.get.transaction) +: accum)
}
} yield scriptSigParams
}
private[wallet] def verifierFromAccept(
accept: DLCAccept): Future[DLCSignatureVerifier] = {
accept: DLCAccept,
transactionDAO: TransactionDAO): Future[DLCSignatureVerifier] = {
for {
dlcDbOpt <- dlcDAO.findByTempContractId(accept.tempContractId)
dlcDb = dlcDbOpt.get
@ -356,7 +352,7 @@ private[bitcoins] trait DLCDataManagement { self: DLCWallet =>
transactionDAO.findByTxIdBEs(localFundingInputs.map(_.outPoint.txIdBE))
} yield {
val offerFundingInputs =
matchPrevTxsWithInputs(localFundingInputs, prevTxs)
DLCTxUtil.matchPrevTxsWithInputs(localFundingInputs, prevTxs)
val offer = dlcOffer.toDLCOffer(contractInfo,
offerFundingInputs,
dlcDb,
@ -369,7 +365,9 @@ private[bitcoins] trait DLCDataManagement { self: DLCWallet =>
}
private[wallet] def verifierFromDb(
contractId: ByteVector): Future[DLCSignatureVerifier] = {
contractId: ByteVector,
transactionDAO: TransactionDAO,
remoteTxDAO: DLCRemoteTxDAO): Future[DLCSignatureVerifier] = {
getDLCFundingData(contractId).flatMap {
case (dlcDb,
contractData,
@ -377,12 +375,16 @@ private[bitcoins] trait DLCDataManagement { self: DLCWallet =>
dlcAccept,
fundingInputsDb,
contractInfo) =>
verifierFromDbData(dlcDb,
contractData,
dlcOffer,
dlcAccept,
fundingInputsDb,
contractInfo)
verifierFromDbData(
dlcDb = dlcDb,
contractData = contractData,
dlcOffer = dlcOffer,
dlcAccept = dlcAccept,
fundingInputsDb = fundingInputsDb,
contractInfo = contractInfo,
transactionDAO = transactionDAO,
remoteTxDAO = remoteTxDAO
)
}
}
@ -392,7 +394,9 @@ private[bitcoins] trait DLCDataManagement { self: DLCWallet =>
dlcOffer: DLCOfferDb,
dlcAccept: DLCAcceptDb,
fundingInputsDb: Vector[DLCFundingInputDb],
contractInfo: ContractInfo): Future[DLCTxBuilder] = {
contractInfo: ContractInfo,
transactionDAO: TransactionDAO,
remoteTxDAO: DLCRemoteTxDAO): Future[DLCTxBuilder] = {
val (localDbFundingInputs, remoteDbFundingInputs) = if (dlcDb.isInitiator) {
(fundingInputsDb.filter(_.isInitiator),
fundingInputsDb.filterNot(_.isInitiator))
@ -407,13 +411,13 @@ private[bitcoins] trait DLCDataManagement { self: DLCWallet =>
remotePrevTxs <-
remoteTxDAO.findByTxIdBEs(remoteDbFundingInputs.map(_.outPoint.txIdBE))
} yield {
val localFundingInputs = matchPrevTxsWithInputs(inputs =
localDbFundingInputs,
prevTxs = localPrevTxs)
val localFundingInputs = DLCTxUtil.matchPrevTxsWithInputs(
inputs = localDbFundingInputs,
prevTxs = localPrevTxs)
val remoteFundingInputs = matchPrevTxsWithInputs(inputs =
remoteDbFundingInputs,
prevTxs = remotePrevTxs)
val remoteFundingInputs = DLCTxUtil.matchPrevTxsWithInputs(
inputs = remoteDbFundingInputs,
prevTxs = remotePrevTxs)
val (offerFundingInputs, acceptFundingInputs) = if (dlcDb.isInitiator) {
(localFundingInputs, remoteFundingInputs)
@ -434,42 +438,36 @@ private[bitcoins] trait DLCDataManagement { self: DLCWallet =>
}
}
/** Takes in a list of inputs to fund DLCs, and pairs them with the full funding transaction for this input
* and then converts the input tx pair to a [[DLCFundingInput]]
* @throws NoSuchElementException when we have an input we cannot find the funding transaction for
*/
private[wallet] def matchPrevTxsWithInputs(
inputs: Vector[DLCFundingInputDb],
prevTxs: Vector[TransactionDb]): Vector[DLCFundingInput] = {
inputs.sortBy(_.index).map { i =>
prevTxs.find(_.txId == i.outPoint.txId) match {
case Some(txDb) => i.toFundingInput(txDb.transaction)
case None =>
throw new NoSuchElementException(
s"Could not find previous transaction with txIdBE=${i.outPoint.txId.flip.hex}")
}
}
}
private[wallet] def verifierFromDbData(
dlcDb: DLCDb,
contractData: DLCContractDataDb,
dlcOffer: DLCOfferDb,
dlcAccept: DLCAcceptDb,
fundingInputsDb: Vector[DLCFundingInputDb],
contractInfo: ContractInfo): Future[DLCSignatureVerifier] = {
contractInfo: ContractInfo,
transactionDAO: TransactionDAO,
remoteTxDAO: DLCRemoteTxDAO): Future[DLCSignatureVerifier] = {
val builderF =
builderFromDbData(dlcDb,
contractData,
dlcOffer,
dlcAccept,
fundingInputsDb,
contractInfo)
builderFromDbData(
dlcDb = dlcDb,
contractDataDb = contractData,
dlcOffer = dlcOffer,
dlcAccept = dlcAccept,
fundingInputsDb = fundingInputsDb,
contractInfo = contractInfo,
transactionDAO = transactionDAO,
remoteTxDAO = remoteTxDAO
)
builderF.map(DLCSignatureVerifier(_, dlcDb.isInitiator))
}
private[wallet] def signerFromDb(dlcId: Sha256Digest): Future[DLCTxSigner] = {
private[wallet] def signerFromDb(
dlcId: Sha256Digest,
transactionDAO: TransactionDAO,
remoteTxDAO: DLCRemoteTxDAO,
fundingUtxoScriptSigParams: Vector[ScriptSignatureParams[InputInfo]],
keyManager: BIP39KeyManager): Future[DLCTxSigner] = {
for {
(dlcDb,
contractData,
@ -478,12 +476,18 @@ private[bitcoins] trait DLCDataManagement { self: DLCWallet =>
fundingInputsDb,
contractInfo) <-
getDLCFundingData(dlcId)
signer <- signerFromDb(dlcDb,
contractData,
dlcOffer,
dlcAccept,
fundingInputsDb,
contractInfo)
signer <- signerFromDb(
dlcDb = dlcDb,
contractDataDb = contractData,
dlcOffer = dlcOffer,
dlcAccept = dlcAccept,
fundingInputsDb = fundingInputsDb,
fundingUtxoScriptSigParams = fundingUtxoScriptSigParams,
contractInfo = contractInfo,
transactionDAO = transactionDAO,
remoteTxDAO = remoteTxDAO,
keyManager = keyManager
)
} yield signer
}
@ -493,15 +497,22 @@ private[bitcoins] trait DLCDataManagement { self: DLCWallet =>
dlcOffer: DLCOfferDb,
dlcAccept: DLCAcceptDb,
fundingInputsDb: Vector[DLCFundingInputDb],
contractInfo: ContractInfo): Future[DLCTxSigner] = {
fundingUtxoScriptSigParams: Vector[ScriptSignatureParams[InputInfo]],
contractInfo: ContractInfo,
transactionDAO: TransactionDAO,
remoteTxDAO: DLCRemoteTxDAO,
keyManager: BIP39KeyManager): Future[DLCTxSigner] = {
for {
fundingUtxos <- fundingUtxosFromDb(dlcDb, fundingInputsDb)
builder <- builderFromDbData(dlcDb = dlcDb,
contractDataDb = contractDataDb,
dlcOffer = dlcOffer,
dlcAccept = dlcAccept,
fundingInputsDb = fundingInputsDb,
contractInfo = contractInfo)
builder <- builderFromDbData(
dlcDb = dlcDb,
contractDataDb = contractDataDb,
dlcOffer = dlcOffer,
dlcAccept = dlcAccept,
fundingInputsDb = fundingInputsDb,
contractInfo = contractInfo,
transactionDAO = transactionDAO,
remoteTxDAO = remoteTxDAO
)
} yield {
val (fundingKey, payoutAddress) = if (dlcDb.isInitiator) {
(dlcOffer.fundingKey, dlcOffer.payoutAddress)
@ -523,7 +534,7 @@ private[bitcoins] trait DLCDataManagement { self: DLCWallet =>
isInitiator = dlcDb.isInitiator,
fundingKey = fundingPrivKey,
finalAddress = payoutAddress,
fundingUtxos = fundingUtxos)
fundingUtxos = fundingUtxoScriptSigParams)
}
}
@ -533,25 +544,47 @@ private[bitcoins] trait DLCDataManagement { self: DLCWallet =>
dlcOffer: DLCOfferDb,
dlcAccept: DLCAcceptDb,
fundingInputsDb: Vector[DLCFundingInputDb],
contractInfo: ContractInfo): Future[DLCExecutor] = {
signerFromDb(dlcDb,
contractDataDb,
dlcOffer,
dlcAccept,
fundingInputsDb,
contractInfo).map(DLCExecutor.apply)
fundingUtxoScriptSigParams: Vector[ScriptSignatureParams[InputInfo]],
contractInfo: ContractInfo,
transactionDAO: TransactionDAO,
remoteTxDAO: DLCRemoteTxDAO,
keyManager: BIP39KeyManager): Future[DLCExecutor] = {
signerFromDb(
dlcDb = dlcDb,
contractDataDb = contractDataDb,
dlcOffer = dlcOffer,
dlcAccept = dlcAccept,
fundingInputsDb = fundingInputsDb,
fundingUtxoScriptSigParams = fundingUtxoScriptSigParams,
contractInfo = contractInfo,
transactionDAO = transactionDAO,
remoteTxDAO = remoteTxDAO,
keyManager = keyManager
).map(DLCExecutor.apply)
}
private[wallet] def executorFromDb(
dlcId: Sha256Digest): Future[DLCExecutor] = {
signerFromDb(dlcId).map(DLCExecutor.apply)
dlcId: Sha256Digest,
transactionDAO: TransactionDAO,
remoteTxDAO: DLCRemoteTxDAO,
fundingUtxoScriptSigParams: Vector[ScriptSignatureParams[InputInfo]],
keyManager: BIP39KeyManager): Future[DLCExecutor] = {
signerFromDb(dlcId = dlcId,
transactionDAO = transactionDAO,
remoteTxDAO = remoteTxDAO,
fundingUtxoScriptSigParams = fundingUtxoScriptSigParams,
keyManager = keyManager).map(DLCExecutor.apply)
}
/** Builds an [[DLCExecutor]] and [[SetupDLC]] for a given contract id
* @return the executor and setup if we still have CET signatures else return None
*/
private[wallet] def executorAndSetupFromDb(
contractId: ByteVector): Future[Option[DLCExecutorWithSetup]] = {
contractId: ByteVector,
transactionDAO: TransactionDAO,
remoteTxDAO: DLCRemoteTxDAO,
fundingUtxoScriptSigParams: Vector[ScriptSignatureParams[InputInfo]],
keyManager: BIP39KeyManager): Future[Option[DLCExecutorWithSetup]] = {
getAllDLCData(contractId).flatMap {
case (dlcDb,
contractData,
@ -563,14 +596,20 @@ private[bitcoins] trait DLCDataManagement { self: DLCWallet =>
outcomeSigsDbsOpt) =>
outcomeSigsDbsOpt match {
case Some(outcomeSigsDbs) =>
executorAndSetupFromDb(dlcDb,
contractData,
dlcOffer,
dlcAccept,
refundSigs,
contractInfo,
fundingInputsDb,
outcomeSigsDbs).map(Some(_))
executorAndSetupFromDb(
dlcDb = dlcDb,
contractDataDb = contractData,
dlcOffer = dlcOffer,
dlcAccept = dlcAccept,
refundSigsDb = refundSigs,
contractInfo = contractInfo,
fundingInputs = fundingInputsDb,
outcomeSigsDbs = outcomeSigsDbs,
transactionDAO = transactionDAO,
remoteTxDAO = remoteTxDAO,
fundingUtxoScriptSigParams = fundingUtxoScriptSigParams,
keyManager = keyManager
).map(Some(_))
case None =>
//means we cannot re-create messages because
//we don't have the cets in the database anymore
@ -589,15 +628,24 @@ private[bitcoins] trait DLCDataManagement { self: DLCWallet =>
refundSigsDb: DLCRefundSigsDb,
contractInfo: ContractInfo,
fundingInputs: Vector[DLCFundingInputDb],
outcomeSigsDbs: Vector[DLCCETSignaturesDb]): Future[
DLCExecutorWithSetup] = {
outcomeSigsDbs: Vector[DLCCETSignaturesDb],
transactionDAO: TransactionDAO,
remoteTxDAO: DLCRemoteTxDAO,
fundingUtxoScriptSigParams: Vector[ScriptSignatureParams[InputInfo]],
keyManager: BIP39KeyManager): Future[DLCExecutorWithSetup] = {
val dlcExecutorF = executorFromDb(dlcDb,
contractDataDb,
dlcOffer,
dlcAccept,
fundingInputs,
contractInfo)
val dlcExecutorF = executorFromDb(
dlcDb = dlcDb,
contractDataDb = contractDataDb,
dlcOffer = dlcOffer,
dlcAccept = dlcAccept,
fundingInputsDb = fundingInputs,
fundingUtxoScriptSigParams = fundingUtxoScriptSigParams,
contractInfo = contractInfo,
transactionDAO = transactionDAO,
remoteTxDAO = remoteTxDAO,
keyManager = keyManager
)
dlcExecutorF.flatMap { executor =>
// Filter for only counterparty's outcome sigs

View file

@ -26,15 +26,28 @@ private[bitcoins] trait DLCTransactionProcessing extends TransactionProcessing {
import dlcDAO.profile.api._
private lazy val safeDatabase: SafeDatabase = dlcDAO.safeDatabase
private lazy val dlcDataManagement: DLCDataManagement = DLCDataManagement(
dlcWalletDAOs)
/** Calculates the new state of the DLCDb based on the closing transaction,
* will delete old CET sigs that are no longer needed after execution
*/
def calculateAndSetState(dlcDb: DLCDb): Future[DLCDb] = {
(dlcDb.contractIdOpt, dlcDb.closingTxIdOpt) match {
case (Some(id), Some(txId)) =>
val executorWithSetupOptF = executorAndSetupFromDb(id)
executorWithSetupOptF.flatMap { case executorWithSetupOpt =>
executorWithSetupOpt match {
val fundingInputsF = dlcInputsDAO.findByDLCId(dlcDb.dlcId)
val scriptSigParamsF =
fundingInputsF.flatMap(inputs => getScriptSigParams(dlcDb, inputs))
for {
scriptSigParams <- scriptSigParamsF
executorWithSetupOpt <- dlcDataManagement.executorAndSetupFromDb(
id,
transactionDAO,
remoteTxDAO,
scriptSigParams,
keyManager)
updatedDlcDb <- executorWithSetupOpt match {
case Some(exeutorWithSetup) =>
calculateAndSetStateWithSetupDLC(exeutorWithSetup.setup,
dlcDb,
@ -44,7 +57,7 @@ private[bitcoins] trait DLCTransactionProcessing extends TransactionProcessing {
//just return the dlcdb given to us
Future.successful(dlcDb)
}
}
} yield updatedDlcDb
case (None, None) | (None, Some(_)) | (Some(_), None) =>
Future.successful(dlcDb)
}
@ -101,13 +114,13 @@ private[bitcoins] trait DLCTransactionProcessing extends TransactionProcessing {
for {
(_, contractData, offerDb, acceptDb, fundingInputDbs, _) <-
getDLCFundingData(dlcId)
dlcDataManagement.getDLCFundingData(dlcId)
txIds = fundingInputDbs.map(_.outPoint.txIdBE)
remotePrevTxs <- remoteTxDAO.findByTxIdBEs(txIds)
localPrevTxs <- transactionDAO.findByTxIdBEs(txIds)
(sigDbs, refundSigsDb) <- getCetAndRefundSigs(dlcId)
(announcements, announcementData, nonceDbs) <- getDLCAnnouncementDbs(
dlcDb.dlcId)
(sigDbs, refundSigsDb) <- dlcDataManagement.getCetAndRefundSigs(dlcId)
(announcements, announcementData, nonceDbs) <- dlcDataManagement
.getDLCAnnouncementDbs(dlcDb.dlcId)
cet <-
transactionDAO
@ -127,10 +140,10 @@ private[bitcoins] trait DLCTransactionProcessing extends TransactionProcessing {
val acceptRefundSigOpt = refundSigsDb.map(_.accepterSig)
val contractInfo =
getContractInfo(contractData,
announcements,
announcementData,
nonceDbs)
dlcDataManagement.getContractInfo(contractData,
announcements,
announcementData,
nonceDbs)
val offer =
offerDb.toDLCOffer(contractInfo, fundingInputs, dlcDb, contractData)
@ -176,9 +189,10 @@ private[bitcoins] trait DLCTransactionProcessing extends TransactionProcessing {
noncesByAnnouncement = nonceDbs
.groupBy(_.announcementId)
announcementsWithId = getOracleAnnouncementsWithId(announcements,
announcementData,
nonceDbs)
announcementsWithId = dlcDataManagement.getOracleAnnouncementsWithId(
announcements,
announcementData,
nonceDbs)
usedIds = announcementsWithId
.filter(t => oracleInfos.exists(_.announcement == t._1))

View file

@ -158,7 +158,7 @@ case class DLCDAO()(implicit
def aggregateSignatureOpt: Rep[Option[SchnorrDigitalSignature]] = column(
"aggregate_signature")
def * : ProvenShape[DLCDb] =
override def * : ProvenShape[DLCDb] =
(dlcId,
tempContractId,
contractId,

View file

@ -0,0 +1,13 @@
package org.bitcoins.dlc.wallet.models
case class DLCWalletDAOs(
dlcDAO: DLCDAO,
contractDataDAO: DLCContractDataDAO,
dlcAnnouncementDAO: DLCAnnouncementDAO,
dlcInputsDAO: DLCFundingInputDAO,
dlcOfferDAO: DLCOfferDAO,
dlcAcceptDAO: DLCAcceptDAO,
dlcSigsDAO: DLCCETSignaturesDAO,
dlcRefundSigDAO: DLCRefundSigsDAO,
oracleNonceDAO: OracleNonceDAO,
oracleAnnouncementDAO: OracleAnnouncementDataDAO)

View file

@ -2,42 +2,25 @@ package org.bitcoins.dlc.wallet.util
import org.bitcoins.core.api.dlc.wallet.db.DLCDb
import org.bitcoins.crypto.{SchnorrDigitalSignature, SchnorrNonce, Sha256Digest}
import org.bitcoins.dlc.wallet.models.{
DLCAcceptDAO,
DLCAcceptDb,
DLCAnnouncementDAO,
DLCAnnouncementDb,
DLCCETSignaturesDAO,
DLCCETSignaturesDb,
DLCContractDataDAO,
DLCContractDataDb,
DLCDAO,
DLCFundingInputDAO,
DLCFundingInputDb,
DLCOfferDAO,
DLCOfferDb,
DLCRefundSigsDAO,
DLCRefundSigsDb,
OracleNonceDAO,
OracleNonceDb
}
import org.bitcoins.dlc.wallet.models._
import scala.concurrent.ExecutionContext
/** Utility class to help build actions to insert things into our DLC tables */
case class DLCActionBuilder(
dlcDAO: DLCDAO,
contractDataDAO: DLCContractDataDAO,
dlcAnnouncementDAO: DLCAnnouncementDAO,
dlcInputsDAO: DLCFundingInputDAO,
dlcOfferDAO: DLCOfferDAO,
dlcAcceptDAO: DLCAcceptDAO,
dlcSigsDAO: DLCCETSignaturesDAO,
dlcRefundSigDAO: DLCRefundSigsDAO,
oracleNonceDAO: OracleNonceDAO) {
case class DLCActionBuilder(dlcWalletDAOs: DLCWalletDAOs) {
private val dlcDAO = dlcWalletDAOs.dlcDAO
private val dlcAnnouncementDAO = dlcWalletDAOs.dlcAnnouncementDAO
private val dlcInputsDAO = dlcWalletDAOs.dlcInputsDAO
private val dlcOfferDAO = dlcWalletDAOs.dlcOfferDAO
private val contractDataDAO = dlcWalletDAOs.contractDataDAO
private val dlcAcceptDAO = dlcWalletDAOs.dlcAcceptDAO
private val dlcSigsDAO = dlcWalletDAOs.dlcSigsDAO
private val dlcRefundSigDAO = dlcWalletDAOs.dlcRefundSigDAO
private val oracleNonceDAO = dlcWalletDAOs.oracleNonceDAO
//idk if it matters which profile api i import, but i need access to transactionally
import dlcDAO.profile.api._
import dlcWalletDAOs.dlcDAO.profile.api._
/** Builds an offer in our database, adds relevant information to the global table,
* contract data, announcements, funding inputs, and the actual offer itself

View file

@ -0,0 +1,26 @@
package org.bitcoins.dlc.wallet.util
import org.bitcoins.core.api.wallet.db.TransactionDb
import org.bitcoins.core.protocol.dlc.models.DLCFundingInput
import org.bitcoins.dlc.wallet.models.DLCFundingInputDb
object DLCTxUtil {
/** Takes in a list of inputs to fund DLCs, and pairs them with the full funding transaction for this input
* and then converts the input tx pair to a [[DLCFundingInput]]
* @throws NoSuchElementException when we have an input we cannot find the funding transaction for
*/
def matchPrevTxsWithInputs(
inputs: Vector[DLCFundingInputDb],
prevTxs: Vector[TransactionDb]): Vector[DLCFundingInput] = {
inputs.sortBy(_.index).map { i =>
prevTxs.find(_.txId == i.outPoint.txId) match {
case Some(txDb) => i.toFundingInput(txDb.transaction)
case None =>
throw new NoSuchElementException(
s"Could not find previous transaction with txIdBE=${i.outPoint.txId.flip.hex}")
}
}
}
}