Add SignDbState to fix rescan exception (#4246)

* Add SignDbState to fix rescan exception

* Cleanup

* Add test, doesn't pass

* Add unit tests, add invariants

* Add unit test to appServerTest

* Fix handling so an exception is not thrown internally when a contractId does not exist
This commit is contained in:
Chris Stewart 2022-04-11 08:06:53 -05:00 committed by GitHub
parent 27cb4a3c20
commit 37da24b94b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 550 additions and 236 deletions

View file

@ -211,12 +211,7 @@ object Server {
)
}
def httpBadRequest(ex: Throwable): HttpResponse = {
httpBadRequest(ex.getMessage)
}
def httpBadRequest(msg: String): HttpResponse = {
def httpError(msg: String): HttpEntity.Strict = {
val entity = {
val response = Response(error = Some(msg))
HttpEntity(
@ -224,6 +219,15 @@ object Server {
up.write(response.toJsonMap)
)
}
entity
}
def httpBadRequest(ex: Throwable): HttpResponse = {
httpBadRequest(ex.getMessage)
}
def httpBadRequest(msg: String): HttpResponse = {
val entity = httpError(msg)
HttpResponse(status = StatusCodes.BadRequest, entity = entity)
}

View file

@ -1175,7 +1175,7 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
(mockWalletApi
.executeDLC(_: ByteVector, _: Seq[OracleAttestmentTLV]))
.expects(contractId, Vector(dummyOracleAttestment))
.returning(Future.successful(EmptyTransaction))
.returning(Future.successful(Some(EmptyTransaction)))
val route = walletRoutes.handleCommand(
ServerCommand("executedlc",
@ -1191,12 +1191,31 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
}
}
"execute a dlc that is not in the wallet and handle gracefully" in {
(mockWalletApi
.executeDLC(_: ByteVector, _: Seq[OracleAttestmentTLV]))
.expects(contractId, Vector(dummyOracleAttestment))
.returning(Future.successful(None))
val route = walletRoutes.handleCommand(
ServerCommand("executedlc",
Arr(Str(contractId.toHex),
Arr(Str(dummyOracleAttestment.hex)),
Bool(true))))
Post() ~> route ~> check {
assert(contentType == `application/json`)
assert(
responseAs[String] == s"""{"result":null,"error":"Cannot execute DLC with contractId=${contractId.toHex}"}""")
}
}
"execute a dlc with multiple sigs" in {
(mockWalletApi
.executeDLC(_: ByteVector, _: Seq[OracleAttestmentTLV]))
.expects(contractId,
Vector(dummyOracleAttestment, dummyOracleAttestment))
.returning(Future.successful(EmptyTransaction))
.returning(Future.successful(Some(EmptyTransaction)))
val route = walletRoutes.handleCommand(
ServerCommand("executedlc",
@ -1217,7 +1236,7 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
(mockWalletApi
.executeDLC(_: ByteVector, _: Seq[OracleAttestmentTLV]))
.expects(contractId, Vector(dummyOracleAttestment))
.returning(Future.successful(EmptyTransaction))
.returning(Future.successful(Some(EmptyTransaction)))
(mockWalletApi.broadcastTransaction _)
.expects(EmptyTransaction)

View file

@ -537,10 +537,25 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit
case Success(ExecuteDLC(contractId, sigs, noBroadcast)) =>
complete {
for {
tx <- wallet.executeDLC(contractId, sigs)
ret <- handleBroadcastable(tx, noBroadcast)
txOpt <- wallet.executeDLC(contractId, sigs)
retOpt <- {
txOpt match {
case Some(tx) =>
handleBroadcastable(tx, noBroadcast)
.map(_.hex)
.map(Some(_))
case None =>
Future.successful(None)
}
}
} yield {
Server.httpSuccess(ret.hex)
retOpt match {
case Some(ret) =>
Server.httpSuccess(ret)
case None =>
Server.httpError(
s"Cannot execute DLC with contractId=${contractId.toHex}")
}
}
}
}

View file

@ -86,24 +86,24 @@ trait DLCWalletApi { self: WalletApi =>
/** Creates the CET for the given contractId and oracle signature, does not broadcast it */
def executeDLC(
contractId: ByteVector,
oracleSig: OracleAttestmentTLV): Future[Transaction] =
oracleSig: OracleAttestmentTLV): Future[Option[Transaction]] =
executeDLC(contractId, Vector(oracleSig))
/** Creates the CET for the given contractId and oracle signature, does not broadcast it */
def executeDLC(
contractId: ByteVector,
oracleSigs: Seq[OracleAttestmentTLV]): Future[Transaction]
oracleSigs: Seq[OracleAttestmentTLV]): Future[Option[Transaction]]
/** Creates the CET for the given contractId and oracle signature, does not broadcast it */
def executeDLC(
contractId: ByteVector,
oracleSig: OracleSignatures): Future[Transaction] =
oracleSig: OracleSignatures): Future[Option[Transaction]] =
executeDLC(contractId, Vector(oracleSig))
/** Creates the CET for the given contractId and oracle signature, does not broadcast it */
def executeDLC(
contractId: ByteVector,
oracleSigs: Vector[OracleSignatures]): Future[Transaction]
oracleSigs: Vector[OracleSignatures]): Future[Option[Transaction]]
/** Creates the refund transaction for the given contractId, does not broadcast it */
def executeDLCRefund(contractId: ByteVector): Future[Transaction]

View file

@ -74,7 +74,7 @@ class DLCExecutionBitcoindBackendTest
s"Cannot retrieve sigs for disjoint union contract, got=$disjoint")
}
}
closingTx <- dlcB.executeDLC(contractId, oracleSigs)
closingTx <- dlcB.executeDLC(contractId, oracleSigs).map(_.get)
//broadcast the closing tx
_ <- dlcB.broadcastTransaction(closingTx)
dlcs <- dlcB

View file

@ -108,7 +108,8 @@ class DLCExecutionTest extends BitcoinSDualWalletTest {
s"Cannot retrieve sigs for disjoint union contract, got=$disjoint")
}
}
func = (wallet: DLCWallet) => wallet.executeDLC(contractId, sig)
func = (wallet: DLCWallet) =>
wallet.executeDLC(contractId, sig).map(_.get)
result <- dlcExecutionTest(wallets = wallets,
asInitiator = true,
@ -153,7 +154,8 @@ class DLCExecutionTest extends BitcoinSDualWalletTest {
s"Cannot retrieve sigs for disjoint union contract, got=$disjoint")
}
}
func = (wallet: DLCWallet) => wallet.executeDLC(contractId, sig)
func = (wallet: DLCWallet) =>
wallet.executeDLC(contractId, sig).map(_.get)
result <- dlcExecutionTest(wallets = wallets,
asInitiator = false,
@ -225,7 +227,8 @@ class DLCExecutionTest extends BitcoinSDualWalletTest {
}
}
func = (wallet: DLCWallet) => wallet.executeDLC(contractId, sig)
func = (wallet: DLCWallet) =>
wallet.executeDLC(contractId, sig).map(_.get)
result <- dlcExecutionTest(wallets = wallets,
asInitiator = true,
@ -406,7 +409,8 @@ class DLCExecutionTest extends BitcoinSDualWalletTest {
s"Cannot retrieve sigs for disjoint union contract, got=$disjoint")
}
}
func = (wallet: DLCWallet) => wallet.executeDLC(contractId, sig)
func = (wallet: DLCWallet) =>
wallet.executeDLC(contractId, sig).map(_.get)
result <- dlcExecutionTest(wallets = wallets,
asInitiator = true,
@ -452,7 +456,7 @@ class DLCExecutionTest extends BitcoinSDualWalletTest {
sigs = badSigs,
outcomes = badOutcomes)
func = (wallet: DLCWallet) =>
wallet.executeDLC(contractId, badAttestment)
wallet.executeDLC(contractId, badAttestment).map(_.get)
result <- dlcExecutionTest(wallets = wallets,
asInitiator = true,

View file

@ -112,7 +112,8 @@ class DLCMultiOracleEnumExecutionTest extends BitcoinSDualWalletTest {
contractId <- getContractId(wallets._1.wallet)
(sig, _) = getSigs
status <- getDLCStatus(wallets._2.wallet)
func = (wallet: DLCWallet) => wallet.executeDLC(contractId, sig)
func = (wallet: DLCWallet) =>
wallet.executeDLC(contractId, sig).map(_.get)
result <- dlcExecutionTest(wallets = wallets,
asInitiator = true,
@ -151,7 +152,8 @@ class DLCMultiOracleEnumExecutionTest extends BitcoinSDualWalletTest {
contractId <- getContractId(wallets._1.wallet)
(_, sig) = getSigs
status <- getDLCStatus(wallets._2.wallet)
func = (wallet: DLCWallet) => wallet.executeDLC(contractId, sig)
func = (wallet: DLCWallet) =>
wallet.executeDLC(contractId, sig).map(_.get)
result <- dlcExecutionTest(wallets = wallets,
asInitiator = false,

View file

@ -105,7 +105,8 @@ class DLCMultiOracleExactNumericExecutionTest extends BitcoinSDualWalletTest {
contractId <- getContractId(wallets._1.wallet)
status <- getDLCStatus(wallets._1.wallet)
(sigs, _) = getSigs(status.contractInfo)
func = (wallet: DLCWallet) => wallet.executeDLC(contractId, sigs)
func = (wallet: DLCWallet) =>
wallet.executeDLC(contractId, sigs).map(_.get)
result <- dlcExecutionTest(wallets = wallets,
asInitiator = true,
@ -144,7 +145,8 @@ class DLCMultiOracleExactNumericExecutionTest extends BitcoinSDualWalletTest {
contractId <- getContractId(wallets._1.wallet)
status <- getDLCStatus(wallets._2.wallet)
(_, sigs) = getSigs(status.contractInfo)
func = (wallet: DLCWallet) => wallet.executeDLC(contractId, sigs)
func = (wallet: DLCWallet) =>
wallet.executeDLC(contractId, sigs).map(_.get)
result <- dlcExecutionTest(wallets = wallets,
asInitiator = false,

View file

@ -114,7 +114,8 @@ class DLCMultiOracleNumericExecutionTest
contractId <- getContractId(wallets._1.wallet)
status <- getDLCStatus(wallets._1.wallet)
(sigs, _) = getSigs(status.contractInfo)
func = (wallet: DLCWallet) => wallet.executeDLC(contractId, sigs)
func = (wallet: DLCWallet) =>
wallet.executeDLC(contractId, sigs).map(_.get)
result <- dlcExecutionTest(wallets = wallets,
asInitiator = true,
@ -153,7 +154,8 @@ class DLCMultiOracleNumericExecutionTest
contractId <- getContractId(wallets._1.wallet)
status <- getDLCStatus(wallets._2.wallet)
(_, sigs) = getSigs(status.contractInfo)
func = (wallet: DLCWallet) => wallet.executeDLC(contractId, sigs)
func = (wallet: DLCWallet) =>
wallet.executeDLC(contractId, sigs).map(_.get)
result <- dlcExecutionTest(wallets = wallets,
asInitiator = false,

View file

@ -91,7 +91,8 @@ class DLCNumericExecutionTest extends BitcoinSDualWalletTest {
contractId <- getContractId(wallets._1.wallet)
status <- getDLCStatus(wallets._1.wallet)
(sigs, _) = getSigs(status.contractInfo)
func = (wallet: DLCWallet) => wallet.executeDLC(contractId, sigs)
func = (wallet: DLCWallet) =>
wallet.executeDLC(contractId, sigs).map(_.get)
result <- dlcExecutionTest(wallets = wallets,
asInitiator = true,
@ -130,7 +131,8 @@ class DLCNumericExecutionTest extends BitcoinSDualWalletTest {
contractId <- getContractId(wallets._1.wallet)
status <- getDLCStatus(wallets._2.wallet)
(_, sigs) = getSigs(status.contractInfo)
func = (wallet: DLCWallet) => wallet.executeDLC(contractId, sigs)
func = (wallet: DLCWallet) =>
wallet.executeDLC(contractId, sigs).map(_.get)
result <- dlcExecutionTest(wallets = wallets,
asInitiator = false,
@ -181,7 +183,7 @@ class DLCNumericExecutionTest extends BitcoinSDualWalletTest {
sigs = badSigs,
outcomes = badOutcomes)
func = (wallet: DLCWallet) =>
wallet.executeDLC(contractId, badAttestment)
wallet.executeDLC(contractId, badAttestment).map(_.get)
result <- dlcExecutionTest(wallets = wallets,
asInitiator = false,

View file

@ -106,7 +106,7 @@ class DLCWalletCallbackTest extends BitcoinSDualWalletTest {
s"Cannot retrieve sigs for disjoint union contract, got=$disjoint")
}
}
transaction <- walletA.executeDLC(contractId, sigs._1)
transaction <- walletA.executeDLC(contractId, sigs._1).map(_.get)
_ <- walletB.processTransaction(transaction, None)
} yield ()

View file

@ -40,7 +40,8 @@ class RescanDLCTest extends DualWalletTestCachedBitcoind {
s"Cannot retrieve sigs for disjoint union contract, got=$disjoint")
}
}
func = (wallet: DLCWallet) => wallet.executeDLC(contractId, sig)
func = (wallet: DLCWallet) =>
wallet.executeDLC(contractId, sig).map(_.get)
result <- dlcExecutionTest(wallets = (walletA, walletB),
asInitiator = true,
@ -79,7 +80,8 @@ class RescanDLCTest extends DualWalletTestCachedBitcoind {
s"Cannot retrieve sigs for disjoint union contract, got=$disjoint")
}
}
func = (wallet: DLCWallet) => wallet.executeDLC(contractId, sig)
func = (wallet: DLCWallet) =>
wallet.executeDLC(contractId, sig).map(_.get)
result <- dlcExecutionTest(wallets = (walletA, walletB),
asInitiator = true,

View file

@ -816,7 +816,8 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
tx <- walletB.broadcastDLCFundingTx(sign.contractId)
_ <- walletA.processTransaction(tx, None)
func = (wallet: DLCWallet) => wallet.executeDLC(sign.contractId, sig)
func = (wallet: DLCWallet) =>
wallet.executeDLC(sign.contractId, sig).map(_.get)
result <- dlcExecutionTest(dlcA = walletA,
dlcB = walletB,
asInitiator = true,

View file

@ -0,0 +1,79 @@
package org.bitcoins.dlc.wallet.internal
import org.bitcoins.core.protocol.dlc.compute.DLCUtil
import org.bitcoins.core.protocol.dlc.models.DLCMessage.DLCOffer
import org.bitcoins.core.protocol.dlc.models.DLCState
import org.bitcoins.dlc.wallet.models.{AcceptDbState, SignDbState}
import org.bitcoins.testkit.wallet.{BitcoinSDualWalletTest, DLCWalletUtil}
import org.bitcoins.testkit.wallet.FundWalletUtil.FundedDLCWallet
import org.scalatest.FutureOutcome
class DLCDataManagementTest extends BitcoinSDualWalletTest {
type FixtureParam = (FundedDLCWallet, FundedDLCWallet)
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
withDualFundedDLCWallets(test)
}
behavior of "DLCDataManagement"
it must "retrieve a acceptdb state from getDLCFundingData" in {
fundedDLCWallets: (FundedDLCWallet, FundedDLCWallet) =>
val walletA = fundedDLCWallets._1.wallet
val walletB = fundedDLCWallets._2.wallet
val offerData: DLCOffer =
DLCWalletUtil.sampleDLCOffer
for {
offer1 <- walletA.createDLCOffer(
offerData.contractInfo,
offerData.collateral,
Some(offerData.feeRate),
offerData.timeouts.contractMaturity.toUInt32,
offerData.timeouts.contractTimeout.toUInt32,
None,
None
)
accept <- walletB.acceptDLCOffer(offer1, None, None)
contractId = DLCUtil.calcContractId(offer1, accept)
acceptDbStateOpt <- walletB.dlcDataManagement.getDLCFundingData(
contractId,
walletA.transactionDAO)
} yield {
assert(acceptDbStateOpt.isDefined)
assert(acceptDbStateOpt.get.isInstanceOf[AcceptDbState])
assert(acceptDbStateOpt.get.state == DLCState.Accepted)
}
}
it must "retrieve a signdb state from getDLCFundingData" in {
fundedDLCWallets: (FundedDLCWallet, FundedDLCWallet) =>
val walletA = fundedDLCWallets._1.wallet
val walletB = fundedDLCWallets._2.wallet
val offerData: DLCOffer =
DLCWalletUtil.sampleDLCOffer
for {
offer1 <- walletA.createDLCOffer(
offerData.contractInfo,
offerData.collateral,
Some(offerData.feeRate),
offerData.timeouts.contractMaturity.toUInt32,
offerData.timeouts.contractTimeout.toUInt32,
None,
None
)
accept <- walletB.acceptDLCOffer(offer1, None, None)
sign <- walletA.signDLC(accept)
signDbStateOpt <- walletA.dlcDataManagement.getDLCFundingData(
sign.contractId,
walletA.transactionDAO)
} yield {
assert(signDbStateOpt.isDefined)
assert(signDbStateOpt.get.isInstanceOf[SignDbState])
assert(signDbStateOpt.get.state == DLCState.Signed)
}
}
}

View file

@ -13,9 +13,9 @@ import org.bitcoins.db.DatabaseDriver._
import org.bitcoins.db._
import org.bitcoins.dlc.wallet.internal.DLCDataManagement
import org.bitcoins.dlc.wallet.models.{
AcceptDbState,
DLCSetupDbState,
OfferedDbState
OfferedDbState,
SetupCompleteDLCDbState
}
import org.bitcoins.wallet.config.WalletAppConfig
import org.bitcoins.wallet.models.TransactionDAO
@ -176,7 +176,7 @@ case class DLCAppConfig(baseDatadir: Path, configOverrides: Vector[Config])(
vec: Vector[DLCSetupDbState]): Vector[DLCDb] = {
vec.map { case state: DLCSetupDbState =>
val updatedDlcDb: DLCDb = state match {
case acceptDbState: AcceptDbState =>
case acceptDbState: SetupCompleteDLCDbState =>
val offer = acceptDbState.offer
val acceptWithoutSigs = acceptDbState.acceptWithoutSigs
val dlcDb = acceptDbState.dlcDb

View file

@ -89,7 +89,7 @@ abstract class DLCWallet
incomingOfferDAO
)
private val dlcDataManagement = DLCDataManagement(dlcWalletDAOs)
private[wallet] val dlcDataManagement = DLCDataManagement(dlcWalletDAOs)
protected lazy val actionBuilder: DLCActionBuilder = {
DLCActionBuilder(dlcWalletDAOs)
@ -482,6 +482,9 @@ abstract class DLCWallet
Future.failed(
new RuntimeException(
s"We cannot accept a DLC we offered, dlcId=${dlcId.hex}"))
case _: SignDbState =>
Future.failed(new RuntimeException(
s"We cannot accept a DLC we have already signed, dlcId=${dlcId.hex}"))
case a: AcceptDbState =>
val dlcPubKeys = DLCUtil.calcDLCPubKeys(
xpub = account.xpub,
@ -1327,17 +1330,17 @@ abstract class DLCWallet
setupStateOpt <- dlcDataManagement.getDLCFundingData(contractId,
txDAO =
transactionDAO)
acceptState = {
complete = {
setupStateOpt.map {
case _: OfferedDbState =>
sys.error(
s"Cannot retrieve funding transaction when DLC is in offered state")
case accept: AcceptDbState => accept
case complete: SetupCompleteDLCDbState => complete
}.get //bad but going to have to save this refactor for future
}
dlcDb = acceptState.dlcDb
dlcDb = complete.dlcDb
//is this right? We don't have counterpart scriptSigParams
fundingInputs = acceptState.allFundingInputs
fundingInputs = complete.allFundingInputs
scriptSigParams <- getScriptSigParams(dlcDb, fundingInputs)
signerOpt <- dlcDataManagement.signerFromDb(
dlcDb = dlcDb,
@ -1423,21 +1426,37 @@ abstract class DLCWallet
override def executeDLC(
contractId: ByteVector,
sigs: Seq[OracleAttestmentTLV]): Future[Transaction] = {
sigs: Seq[OracleAttestmentTLV]): Future[Option[Transaction]] = {
logger.info(
s"Executing dlc with contractId=${contractId.toHex} sigs=${sigs.map(_.hex)}")
require(sigs.nonEmpty, "Must provide at least one oracle signature")
val dlcDbOpt = dlcDAO.findByContractId(contractId)
for {
dlcDb <- dlcDAO.findByContractId(contractId).map(_.get)
_ = dlcDb.state match {
case state @ (Offered | AcceptComputingAdaptorSigs | Accepted |
SignComputingAdaptorSigs | Signed) =>
sys.error(
s"Cannot execute DLC before the DLC is broadcast to the blockchain, state=$state")
case Broadcasted | Confirmed | _: ClosedState =>
//can continue executing, do nothing
dlcDbOpt <- dlcDbOpt
txOpt <- {
dlcDbOpt match {
case Some(dlcDb) =>
executeDLC(dlcDb, sigs)
case None =>
Future.successful(None)
}
}
} yield txOpt
}
def executeDLC(
dlcDb: DLCDb,
sigs: Seq[OracleAttestmentTLV]): Future[Option[Transaction]] = {
val _ = dlcDb.state match {
case state @ (Offered | AcceptComputingAdaptorSigs | Accepted |
SignComputingAdaptorSigs | Signed) =>
sys.error(
s"Cannot execute DLC before the DLC is broadcast to the blockchain, state=$state")
case Broadcasted | Confirmed | _: ClosedState =>
//can continue executing, do nothing
}
for {
(announcements, announcementData, nonceDbs) <- dlcDataManagement
.getDLCAnnouncementDbs(dlcDb.dlcId)
@ -1450,13 +1469,13 @@ abstract class DLCWallet
announcements = announcementTLVs,
attestments = sigs.toVector)
tx <- executeDLC(contractId, oracleSigs)
tx <- executeDLC(dlcDb.contractIdOpt.get, oracleSigs)
} yield tx
}
override def executeDLC(
contractId: ByteVector,
oracleSigs: Vector[OracleSignatures]): Future[Transaction] = {
oracleSigs: Vector[OracleSignatures]): Future[Option[Transaction]] = {
require(oracleSigs.nonEmpty, "Must provide at least one oracle signature")
dlcDAO.findByContractId(contractId).flatMap {
case None =>
@ -1467,7 +1486,7 @@ abstract class DLCWallet
db.closingTxIdOpt match {
case Some(txId) =>
transactionDAO.findByTxId(txId).flatMap {
case Some(tx) => Future.successful(tx.transaction)
case Some(tx) => Future.successful(Some(tx.transaction))
case None => createDLCExecutionTx(contractId, oracleSigs)
}
case None =>
@ -1476,60 +1495,69 @@ abstract class DLCWallet
}
}
/** Returns a execution transaction if we can build one. Returns None
* if the dlc db state is incorrect, or we have pruned CET signatures
* from the database
*/
private def createDLCExecutionTx(
contractId: ByteVector,
oracleSigs: Vector[OracleSignatures]): Future[Transaction] = {
oracleSigs: Vector[OracleSignatures]): Future[Option[Transaction]] = {
require(oracleSigs.nonEmpty, "Must provide at least one oracle signature")
for {
setupStateOpt <-
dlcDataManagement.getDLCFundingData(contractId, txDAO = transactionDAO)
_ = require(setupStateOpt.isDefined,
s"Must have setup state defined to create execution tx")
_ = require(
setupStateOpt.get.isInstanceOf[AcceptDbState],
s"Setup state must be accept to create dlc execution tx, got=${setupStateOpt.get.state}")
setupState = setupStateOpt.get.asInstanceOf[AcceptDbState]
dlcDb = setupState.dlcDb
fundingInputs = setupState.allFundingInputs
scriptSigParams <- getScriptSigParams(dlcDb, fundingInputs)
executorWithSetupOpt <- dlcDataManagement.executorAndSetupFromDb(
contractId = contractId,
txDAO = transactionDAO,
fundingUtxoScriptSigParams = scriptSigParams,
keyManager = keyManager)
tx <- {
executorWithSetupOpt match {
case Some(executorWithSetup) =>
buildExecutionTxWithExecutor(executorWithSetup,
oracleSigs,
contractId)
case None =>
//means we don't have cet sigs in the db anymore
//can we retrieve the CET some other way?
val setupStateOptF =
dlcDataManagement.getDLCFundingData(contractId, txDAO = transactionDAO)
setupStateOptF.flatMap {
case None => Future.successful(None)
case Some(setupDbState) =>
setupDbState match {
case o: OfferedDbState =>
logger.info(
s"Cannot create execution tx for dlc in state=${o.state}")
Future.successful(None)
case c: SetupCompleteDLCDbState =>
val dlcDb = c.dlcDb
val fundingInputs = c.allFundingInputs
val scriptSigParamsF = getScriptSigParams(dlcDb, fundingInputs)
val executorWithSetupOptF = scriptSigParamsF.flatMap {
scriptSigParams =>
dlcDataManagement.executorAndSetupFromDb(
contractId = contractId,
txDAO = transactionDAO,
fundingUtxoScriptSigParams = scriptSigParams,
keyManager = keyManager)
}
executorWithSetupOptF.flatMap {
case Some(executorWithSetup) =>
buildExecutionTxWithExecutor(executorWithSetup,
oracleSigs,
contractId).map(Some(_))
case None =>
//means we don't have cet sigs in the db anymore
//can we retrieve the CET some other way?
//lets try to retrieve it from our transactionDAO
val dlcDbOptF = dlcDAO.findByContractId(contractId)
//lets try to retrieve it from our transactionDAO
val dlcDbOptF = dlcDAO.findByContractId(contractId)
for {
dlcDbOpt <- dlcDbOptF
_ = require(
dlcDbOpt.isDefined,
s"Could not find dlc associated with this contractId=${contractId.toHex}")
dlcDb = dlcDbOpt.get
_ = require(
dlcDb.closingTxIdOpt.isDefined,
s"If we don't have CET signatures, the closing tx must be defined, contractId=${contractId.toHex}")
closingTxId = dlcDb.closingTxIdOpt.get
closingTxOpt <- transactionDAO.findByTxId(closingTxId)
} yield {
require(
closingTxOpt.isDefined,
s"Could not find closing tx for DLC in db, contactId=${contractId.toHex} closingTxId=${closingTxId.hex}")
closingTxOpt.get.transaction
for {
dlcDbOpt <- dlcDbOptF
_ = require(
dlcDbOpt.isDefined,
s"Could not find dlc associated with this contractId=${contractId.toHex}")
dlcDb = dlcDbOpt.get
_ = require(
dlcDb.closingTxIdOpt.isDefined,
s"If we don't have CET signatures, the closing tx must be defined, contractId=${contractId.toHex}, state=${dlcDb.state}"
)
closingTxId = dlcDb.closingTxIdOpt.get
closingTxOpt <- transactionDAO.findByTxId(closingTxId)
} yield {
require(
closingTxOpt.isDefined,
s"Could not find closing tx for DLC in db, contactId=${contractId.toHex} closingTxId=${closingTxId.hex}")
Some(closingTxOpt.get.transaction)
}
}
}
}
} yield tx
}
}
private def buildExecutionTxWithExecutor(

View file

@ -269,6 +269,37 @@ case class DLCDataManagement(dlcWalletDAOs: DLCWalletDAOs)(implicit
}
}
private def buildAcceptDbState(
offerDbState: OfferedDbState,
dlcAccept: DLCAcceptDb,
acceptInputs: Vector[DLCFundingInputDb],
cetSignatures: Vector[DLCCETSignaturesDb],
refundSigDb: DLCRefundSigsDb,
txDAO: TransactionDAO): Future[AcceptDbState] = {
val signaturesOpt = {
if (cetSignatures.isEmpty) {
//means we have pruned signatures from the database
//we have to return None
None
} else {
Some(cetSignatures)
}
}
val acceptPrevTxsDbF =
getAcceptPrevTxs(offerDbState.dlcDb, acceptInputs, txDAO)
acceptPrevTxsDbF.map { case acceptPrevTxs =>
offerDbState.toAcceptDb(
acceptDb = dlcAccept,
acceptFundingInputsDb = acceptInputs,
acceptPrevTxsDb = acceptPrevTxs,
cetSigsOpt = signaturesOpt,
refundSigDb = refundSigDb
)
}
}
private[wallet] def getDLCFundingData(
dlcId: Sha256Digest,
txDAO: TransactionDAO): Future[Option[DLCSetupDbState]] = {
@ -282,40 +313,54 @@ case class DLCDataManagement(dlcWalletDAOs: DLCWalletDAOs)(implicit
for {
offerDbState <- offerDbStateOpt
} yield {
//if the accept message is defined we must have refund sigs
dlcAcceptOpt.zip(refundSigsOpt).headOption match {
case Some((dlcAccept, refundSigDb)) =>
require(
refundSigsOpt.isDefined,
s"Cannot have accept in the database if we do not have refund signatures, dlcId=${dlcId.hex}")
val outcomeSigs = cetSignatures.map { dbSig =>
dbSig.sigPoint -> dbSig.accepterSig
}
val signaturesOpt = {
if (cetSignatures.isEmpty) {
//means we have pruned signatures from the database
//we have to return None
None
} else {
val sigs = CETSignatures(outcomeSigs)
Some(sigs)
}
}
val acceptPrevTxsDbF =
getAcceptPrevTxs(offerDbState.dlcDb, acceptInputs, txDAO)
acceptPrevTxsDbF.map { case acceptPrevTxs =>
offerDbState.toAcceptDb(
acceptDb = dlcAccept,
acceptFundingInputsDb = acceptInputs,
acceptPrevTxsDb = acceptPrevTxs,
cetSignaturesOpt = signaturesOpt,
refundSigDb = refundSigDb
)
}
case None =>
//just return the offerDbState if we don't have an accept
offerDbState.dlcDb.state match {
case DLCState.Offered | DLCState.AcceptComputingAdaptorSigs =>
Future.successful(offerDbState)
case DLCState.Accepted | DLCState.SignComputingAdaptorSigs =>
//if the accept message is defined we must have refund sigs
dlcAcceptOpt.zip(refundSigsOpt).headOption match {
case Some((dlcAccept, refundSigDb)) =>
require(
refundSigsOpt.isDefined,
s"Cannot have accept in the database if we do not have refund signatures, dlcId=${dlcId.hex}")
buildAcceptDbState(offerDbState = offerDbState,
dlcAccept = dlcAccept,
acceptInputs = acceptInputs,
cetSignatures = cetSignatures,
refundSigDb = refundSigDb,
txDAO = txDAO)
case None =>
//just return the offerDbState if we don't have an accept
Future.successful(offerDbState)
}
case DLCState.Signed | DLCState.Confirmed | DLCState.Broadcasted |
_: DLCState.ClosedState =>
//if the accept message is defined we must have refund sigs
dlcAcceptOpt.zip(refundSigsOpt).headOption match {
case Some((dlcAccept, refundSigDb)) =>
require(
refundSigsOpt.isDefined,
s"Cannot have accept in the database if we do not have refund signatures, dlcId=${dlcId.hex}")
val acceptDbStateF = buildAcceptDbState(
offerDbState = offerDbState,
dlcAccept = dlcAccept,
acceptInputs = acceptInputs,
cetSignatures = cetSignatures,
refundSigDb = refundSigDb,
txDAO = txDAO)
val signDbF = for {
acceptDbState <- acceptDbStateF
} yield {
acceptDbState.toSignDbOpt.get
}
signDbF
case None =>
//just return the offerDbState if we don't have an accept
Future.successful(offerDbState)
}
}
}
}
@ -330,7 +375,7 @@ case class DLCDataManagement(dlcWalletDAOs: DLCWalletDAOs)(implicit
private[wallet] def getAllDLCData(
contractId: ByteVector,
txDAO: TransactionDAO): Future[Option[DLCClosedDbState]] = {
txDAO: TransactionDAO): Future[Option[DLCDbState]] = {
val resultF = for {
dlcDbOpt <- dlcDAO.findByContractId(contractId)
closedDbStateOptNested = dlcDbOpt.map(d => getAllDLCData(d.dlcId, txDAO))
@ -345,7 +390,7 @@ case class DLCDataManagement(dlcWalletDAOs: DLCWalletDAOs)(implicit
private[wallet] def getAllDLCData(
dlcId: Sha256Digest,
txDAO: TransactionDAO): Future[Option[DLCClosedDbState]] = {
txDAO: TransactionDAO): Future[Option[DLCDbState]] = {
val sigDLCsF = dlcSigsDAO.findByDLCId(dlcId)
for {
@ -356,12 +401,27 @@ case class DLCDataManagement(dlcWalletDAOs: DLCWalletDAOs)(implicit
val sigsOpt = if (sigs.isEmpty) None else Some(sigs)
val closedState = setupStateOpt.flatMap {
case acceptState: AcceptDbState =>
val closedState =
DLCClosedDbState.fromSetupState(acceptState, sigsOpt)
Some(closedState)
case _: OfferedDbState =>
//cannot return a closed state because we haven't seen the accept message
None
acceptState.state match {
case _: DLCState.ClosedState =>
val closedState =
DLCClosedDbState.fromCompleteSetupState(acceptState, sigsOpt)
Some(closedState)
case _: DLCState.InProgressState =>
Some(acceptState)
}
case signState: SignDbState =>
signState.state match {
case _: DLCState.ClosedState =>
val closedState =
DLCClosedDbState.fromCompleteSetupState(signState, sigsOpt)
Some(closedState)
case _: DLCState.InProgressState =>
Some(signState)
}
case o: OfferedDbState =>
Some(o)
}
closedState
}
@ -409,12 +469,13 @@ case class DLCDataManagement(dlcWalletDAOs: DLCWalletDAOs)(implicit
def getOfferAndAcceptWithoutSigs(
dlcId: Sha256Digest,
txDAO: TransactionDAO): Future[Option[AcceptDbState]] = {
txDAO: TransactionDAO): Future[Option[SetupCompleteDLCDbState]] = {
val dataF: Future[Option[DLCSetupDbState]] = getDLCFundingData(dlcId, txDAO)
dataF.map {
case Some(setupDbState) =>
setupDbState match {
case a: AcceptDbState => Some(a)
case s: SignDbState => Some(s)
case _: OfferedDbState => None
}
case None => None
@ -427,9 +488,10 @@ case class DLCDataManagement(dlcWalletDAOs: DLCWalletDAOs)(implicit
for {
setupStateOpt <- getOfferAndAcceptWithoutSigs(dlcDb.dlcId, transactionDAO)
} yield {
setupStateOpt.map { acceptDbState =>
val txBuilder = DLCTxBuilder(offer = acceptDbState.offer,
accept = acceptDbState.acceptWithoutSigs)
setupStateOpt.map { completeSetupDLCDbState =>
val txBuilder =
DLCTxBuilder(offer = completeSetupDLCDbState.offer,
accept = completeSetupDLCDbState.acceptWithoutSigs)
txBuilder
}
}
@ -573,10 +635,32 @@ case class DLCDataManagement(dlcWalletDAOs: DLCWalletDAOs)(implicit
fundingUtxoScriptSigParams = fundingUtxoScriptSigParams,
keyManager = keyManager
)
case c: SetupCompleteDLCDbState =>
c.cetSigsOpt match {
case Some(cetSigs) =>
executorAndSetupFromDb(
dlcDb = c.dlcDb,
refundSigsDb = c.refundSigDb,
fundingInputs = c.allFundingInputs,
outcomeSigsDbs = cetSigs,
transactionDAO = txDAO,
fundingUtxoScriptSigParams = fundingUtxoScriptSigParams,
keyManager = keyManager
)
case None =>
//we don't have cet signatures for the
//sign message any more
Future.successful(None)
}
case _: ClosedDbStateNoCETSigs =>
//means we cannot re-create messages because
//we don't have the cets in the database anymore
Future.successful(None)
case _: OfferedDbState =>
//means we cannot recreate messages because
//we don't have an accept or sign message in the database
Future.successful(None)
}
case None => Future.successful(None)
}

View file

@ -30,9 +30,6 @@ private[bitcoins] trait DLCTransactionProcessing extends TransactionProcessing {
self: DLCWallet =>
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
* @return a DLCDb if we can calculate the state, else None if we cannot calculate the state
@ -147,9 +144,9 @@ private[bitcoins] trait DLCTransactionProcessing extends TransactionProcessing {
setupStateOpt <- dlcDataManagement.getDLCFundingData(dlcId,
txDAO =
transactionDAO)
acceptDbState = {
completeDbState = {
setupStateOpt.get match {
case accept: AcceptDbState => accept
case c: SetupCompleteDLCDbState => c
case offered: OfferedDbState =>
sys.error(
s"Cannot calculate and set outcome of dlc that is only offered, id=${offered.dlcDb.dlcId.hex}")
@ -165,7 +162,7 @@ private[bitcoins] trait DLCTransactionProcessing extends TransactionProcessing {
.map(_.get.transaction.asInstanceOf[WitnessTransaction])
sigAndOutcome = recoverSigAndOutcomeForRemoteClaimed(
acceptDbState = acceptDbState,
completeDbState = completeDbState,
cet = cet,
sigDbs = sigDbs,
refundSigsDbOpt = refundSigOpt)
@ -384,25 +381,25 @@ private[bitcoins] trait DLCTransactionProcessing extends TransactionProcessing {
* so we do not necessarily have access to what the [[OracleAttestment]] is
*/
private def recoverSigAndOutcomeForRemoteClaimed(
acceptDbState: AcceptDbState,
completeDbState: SetupCompleteDLCDbState,
cet: WitnessTransaction,
sigDbs: Vector[DLCCETSignaturesDb],
refundSigsDbOpt: Option[DLCRefundSigsDb]): (
SchnorrDigitalSignature,
OracleOutcome) = {
val dlcDb = acceptDbState.dlcDb
val dlcDb = completeDbState.dlcDb
val dlcId = dlcDb.dlcId
val isInit = dlcDb.isInitiator
val offer = acceptDbState.offer
val offer = completeDbState.offer
val acceptOpt = acceptDbState.acceptOpt
val acceptOpt = completeDbState.acceptOpt
require(
acceptOpt.isDefined,
s"Accept message must still have CET signatures to recover an outcome on chain, dlcId=${dlcId.hex}")
val accept = acceptOpt.get
val fundingInputDbs = acceptDbState.allFundingInputs
val fundingInputDbs = completeDbState.allFundingInputs
val offerRefundSigOpt = refundSigsDbOpt.flatMap(_.initiatorSig)
val signOpt: Option[DLCSign] = offerRefundSigOpt.map { refundSig =>

View file

@ -7,10 +7,6 @@ import org.bitcoins.core.protocol.dlc.models.DLCMessage.{
DLCAcceptWithoutSigs,
DLCOffer
}
import org.bitcoins.core.protocol.dlc.models.DLCState.{
ClosedState,
InProgressState
}
import org.bitcoins.core.protocol.dlc.models.{
CETSignatures,
ContractInfo,
@ -39,19 +35,17 @@ sealed trait DLCDbState {
contractDataDb = contractDataDb)
}
def state: DLCState
final def state: DLCState = dlcDb.state
}
/** Represents a DLC in the database that
* has not had its funding transaction published.
* This means we are still setting up the DLC
*/
sealed trait DLCSetupDbState extends DLCDbState {
override def state: InProgressState
}
sealed trait DLCSetupDbState extends DLCDbState
/** Represents a DLC in the database that has
* been fully setup and is in progress
* been fully setup and settled
*/
sealed trait DLCClosedDbState extends DLCDbState
@ -63,10 +57,6 @@ case class OfferedDbState(
offerFundingInputsDb: Vector[DLCFundingInputDb],
offerPrevTxs: Vector[TransactionDb])
extends DLCSetupDbState {
//require(dlcDb.state == DLCState.Offered,
// s"OfferedDbState requires state offered, got=${dlcDb.state}")
override val state: DLCState.Offered.type = DLCState.Offered
/** Converts a [[OfferedDbState]] to an [[AcceptDbState]]
* @param acceptDb
@ -78,7 +68,7 @@ case class OfferedDbState(
acceptDb: DLCAcceptDb,
acceptFundingInputsDb: Vector[DLCFundingInputDb],
acceptPrevTxsDb: Vector[TransactionDb],
cetSignaturesOpt: Option[CETSignatures],
cetSigsOpt: Option[Vector[DLCCETSignaturesDb]],
refundSigDb: DLCRefundSigsDb): AcceptDbState = {
AcceptDbState(
dlcDb = dlcDb,
@ -90,12 +80,78 @@ case class OfferedDbState(
offerPrevTxs = offerPrevTxs,
acceptFundingInputsDb = acceptFundingInputsDb,
acceptPrevTxs = acceptPrevTxsDb,
cetSignaturesOpt = cetSignaturesOpt,
cetSigsOpt = cetSigsOpt,
refundSigDb = refundSigDb
)
}
}
/** Shared data structured when we have all information to build a funding
* transaction for a discreet log contract
*/
sealed trait SetupCompleteDLCDbState extends DLCSetupDbState {
def dlcDb: DLCDb
def contractDataDb: DLCContractDataDb
def contractInfo: ContractInfo
def offerDb: DLCOfferDb
def acceptDb: DLCAcceptDb
def offerFundingInputsDb: Vector[DLCFundingInputDb]
def offerPrevTxs: Vector[TransactionDb]
def acceptFundingInputsDb: Vector[DLCFundingInputDb]
def acceptPrevTxs: Vector[TransactionDb]
def refundSigDb: DLCRefundSigsDb
def cetSigsOpt: Option[Vector[DLCCETSignaturesDb]]
def allFundingInputs: Vector[DLCFundingInputDb]
def acceptFundingInputs: Vector[DLCFundingInput] = {
DLCTxUtil.matchPrevTxsWithInputs(acceptFundingInputsDb, acceptPrevTxs)
}
def acceptWithoutSigs: DLCAcceptWithoutSigs = {
acceptDb.toDLCAcceptWithoutSigs(
tempContractId = dlcDb.tempContractId,
fundingInputs = acceptFundingInputs
)
}
def cetSignaturesOpt: Option[CETSignatures] = {
cetSigsOpt.map { cetSigs =>
this match {
case _: AcceptDbState =>
acceptCETSigsOpt.get
case _: SignDbState =>
CETSignatures(cetSigs.map(c => (c.sigPoint, c.initiatorSig.get)))
}
}
}
def acceptCETSigsOpt: Option[CETSignatures] = {
cetSigsOpt.map { cetSigs =>
CETSignatures(cetSigs.map(c => (c.sigPoint, c.accepterSig)))
}
}
def offererCETSigsOpt: Option[CETSignatures] = {
cetSigsOpt.map { cetSigs =>
CETSignatures(cetSigs.map(c => (c.sigPoint, c.initiatorSig.get)))
}
}
/** Reconstructs the [[DLCAccept]] message if we have [[CETSignatures]]
* in the database. If we don't have the signatures because we have pruned
* them we return None as we can't reconstruct the message
*/
def acceptOpt: Option[DLCAccept] = {
acceptCETSigsOpt.map { cetSignatures =>
acceptDb.toDLCAccept(dlcDb.tempContractId,
acceptFundingInputs,
outcomeSigs = cetSignatures.outcomeSigs,
refundSig = refundSigDb.accepterSig)
}
}
}
case class AcceptDbState(
dlcDb: DLCDb,
contractDataDb: DLCContractDataDb,
@ -106,26 +162,11 @@ case class AcceptDbState(
offerPrevTxs: Vector[TransactionDb],
acceptFundingInputsDb: Vector[DLCFundingInputDb],
acceptPrevTxs: Vector[TransactionDb],
cetSignaturesOpt: Option[CETSignatures],
cetSigsOpt: Option[Vector[DLCCETSignaturesDb]],
refundSigDb: DLCRefundSigsDb)
extends DLCSetupDbState {
//require(dlcDb.state == DLCState.Accepted,
// s"OfferedDbState requires state accepted, got=${dlcDb.state}")
extends SetupCompleteDLCDbState {
override val state: DLCState.Accepted.type = DLCState.Accepted
val acceptFundingInputs: Vector[DLCFundingInput] = {
DLCTxUtil.matchPrevTxsWithInputs(acceptFundingInputsDb, acceptPrevTxs)
}
val acceptWithoutSigs: DLCAcceptWithoutSigs = {
acceptDb.toDLCAcceptWithoutSigs(
tempContractId = dlcDb.tempContractId,
fundingInputs = acceptFundingInputs
)
}
val allFundingInputs: Vector[DLCFundingInputDb] =
override val allFundingInputs: Vector[DLCFundingInputDb] =
offerFundingInputsDb ++ acceptFundingInputsDb
val remotePrevTxs: Vector[TransactionDb] = {
@ -138,20 +179,64 @@ case class AcceptDbState(
else acceptPrevTxs
}
/** Reconstructs the [[DLCAccept]] message if we have [[CETSignatures]]
* in the database. If we don't have the signatures because we have pruned
* them we return None as we can't reconstruct the message
/** Converts the AcceptDbState -> SignDbState if we have
* all parties CET signatures and refund signatures
*/
def acceptOpt: Option[DLCAccept] = {
cetSignaturesOpt.map { cetSignatures =>
acceptDb.toDLCAccept(dlcDb.tempContractId,
acceptFundingInputs,
outcomeSigs = cetSignatures.outcomeSigs,
refundSig = refundSigDb.accepterSig)
def toSignDbOpt: Option[SignDbState] = {
//if we haven't pruned CET signatures from the db
//they must have the offerer's CET signatures defined
cetSigsOpt.map { cetSigs =>
require(cetSigs.forall(_.initiatorSig.isDefined),
s"CET signatures must be defined for the offerer")
}
//if we don't have a refund signature from the offerer
//yet we haven't completed the sign message
refundSigDb.initiatorSig.map { _ =>
val sign = SignDbState(
dlcDb,
contractDataDb,
contractInfo,
offerDb,
acceptDb = acceptDb,
offerFundingInputsDb = offerFundingInputsDb,
offerPrevTxs = offerPrevTxs,
acceptFundingInputsDb = acceptFundingInputsDb,
acceptPrevTxs = acceptPrevTxs,
refundSigDb = refundSigDb,
cetSigsOpt = cetSigsOpt
)
sign
}
}
}
case class SignDbState(
dlcDb: DLCDb,
contractDataDb: DLCContractDataDb,
contractInfo: ContractInfo,
offerDb: DLCOfferDb,
acceptDb: DLCAcceptDb,
offerFundingInputsDb: Vector[DLCFundingInputDb],
offerPrevTxs: Vector[TransactionDb],
acceptFundingInputsDb: Vector[DLCFundingInputDb],
acceptPrevTxs: Vector[TransactionDb],
refundSigDb: DLCRefundSigsDb,
cetSigsOpt: Option[Vector[DLCCETSignaturesDb]]
) extends SetupCompleteDLCDbState {
require(refundSigDb.initiatorSig.isDefined,
s"Refund signature for offerer must be defined")
//If we have not prune CET signatures, the offerer CET signatures must be defined
cetSigsOpt.map(cetSigs =>
require(cetSigs.forall(_.initiatorSig.isDefined),
s"Offerer CET signatures must be defined when in SignDbState"))
override val allFundingInputs: Vector[DLCFundingInputDb] =
offerFundingInputsDb ++ acceptFundingInputsDb
}
case class ClosedDbStateWithCETSigs(
dlcDb: DLCDb,
contractDataDb: DLCContractDataDb,
@ -165,11 +250,6 @@ case class ClosedDbStateWithCETSigs(
refundSigsDb: DLCRefundSigsDb,
cetSigs: Vector[DLCCETSignaturesDb]
) extends DLCClosedDbState {
//require(
// dlcDb.state.isInstanceOf[DLCState.ClosedState],
// s"ClosedDbStateWithCETSigs dlc state must be closed, got=${dlcDb.state}")
override val state: DLCState = dlcDb.state
val allFundingInputs: Vector[DLCFundingInputDb] =
offerFundingInputsDb ++ acceptFundingInputsDb
@ -189,47 +269,40 @@ case class ClosedDbStateNoCETSigs(
acceptFundingInputsDb: Vector[DLCFundingInputDb],
acceptPrevTxs: Vector[TransactionDb],
refundSigsDb: DLCRefundSigsDb)
extends DLCClosedDbState {
//require(
// dlcDb.state.isInstanceOf[DLCState.ClosedState],
// s"ClosedDbStateWithCETSigs dlc state must be closed, got=${dlcDb.state}")
override val state: ClosedState =
dlcDb.state.asInstanceOf[DLCState.ClosedState]
}
extends DLCClosedDbState
object DLCClosedDbState {
def fromSetupState(
acceptState: AcceptDbState,
def fromCompleteSetupState(
completeState: SetupCompleteDLCDbState,
cetSigsOpt: Option[Vector[DLCCETSignaturesDb]]): DLCClosedDbState = {
cetSigsOpt match {
case Some(cetSigs) =>
ClosedDbStateWithCETSigs(
acceptState.dlcDb,
acceptState.contractDataDb,
acceptState.contractInfo,
acceptState.offerDb,
acceptState.acceptDb,
acceptState.offerFundingInputsDb,
acceptState.offerPrevTxs,
acceptState.acceptFundingInputsDb,
acceptState.acceptPrevTxs,
acceptState.refundSigDb,
completeState.dlcDb,
completeState.contractDataDb,
completeState.contractInfo,
completeState.offerDb,
completeState.acceptDb,
completeState.offerFundingInputsDb,
completeState.offerPrevTxs,
completeState.acceptFundingInputsDb,
completeState.acceptPrevTxs,
completeState.refundSigDb,
cetSigs
)
case None =>
ClosedDbStateNoCETSigs(
acceptState.dlcDb,
acceptState.contractDataDb,
acceptState.contractInfo,
acceptState.offerDb,
acceptState.acceptDb,
acceptState.offerFundingInputsDb,
acceptState.offerPrevTxs,
acceptState.acceptFundingInputsDb,
acceptState.acceptPrevTxs,
acceptState.refundSigDb
completeState.dlcDb,
completeState.contractDataDb,
completeState.contractInfo,
completeState.offerDb,
completeState.acceptDb,
completeState.offerFundingInputsDb,
completeState.offerPrevTxs,
completeState.acceptFundingInputsDb,
completeState.acceptPrevTxs,
completeState.refundSigDb
)
}
}