2022 10 05 Delete legacy DLCSerializationVersion.Alpha DLCs for a cleaner upgrade to v0 spec (#4817)

* Delete alpha DLCs

* Get alpha migration working
This commit is contained in:
Chris Stewart 2022-10-07 07:13:32 -05:00 committed by GitHub
parent c075112db5
commit 62081a43ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 61 additions and 87 deletions

View file

@ -5,6 +5,7 @@ import org.bitcoins.core.api.wallet.db.TransactionDbHelper
import org.bitcoins.core.currency.Satoshis
import org.bitcoins.core.number.{UInt32, UInt64}
import org.bitcoins.core.protocol.script.EmptyScriptPubKey
import org.bitcoins.core.protocol.tlv.DLCSerializationVersion
import org.bitcoins.core.protocol.transaction.{
TransactionConstants,
TransactionOutPoint,
@ -261,4 +262,16 @@ class DLCDAOTest extends BitcoinSWalletTest with DLCDAOFixture {
assert(deleted.get.peerOpt.isEmpty)
}
}
it must "read an alpha version of a DLC from the database" in { daos =>
val alphaDLCDb =
dlcDb.copy(serializationVersion = DLCSerializationVersion.Alpha)
for {
_ <- daos.dlcDAO.create(alphaDLCDb)
foundOpt <- daos.dlcDAO.findByDLCSerializationVersion(
DLCSerializationVersion.Alpha)
} yield {
assert(foundOpt.nonEmpty)
}
}
}

View file

@ -4,23 +4,20 @@ import akka.actor.ActorSystem
import com.typesafe.config.Config
import org.bitcoins.commons.config.{AppConfigFactoryBase, ConfigOps}
import org.bitcoins.core.api.chain.ChainQueryApi
import org.bitcoins.core.api.dlc.wallet.db.DLCDb
import org.bitcoins.core.api.feeprovider.FeeRateApi
import org.bitcoins.core.api.node.NodeApi
import org.bitcoins.core.protocol.dlc.compute.DLCUtil
import org.bitcoins.core.protocol.dlc.models.DLCState
import org.bitcoins.core.protocol.dlc.models.DLCState.{
AdaptorSigComputationState,
ClosedState
}
import org.bitcoins.core.protocol.tlv.DLCSerializationVersion
import org.bitcoins.core.util.Mutable
import org.bitcoins.db.DatabaseDriver._
import org.bitcoins.db._
import org.bitcoins.dlc.wallet.internal.DLCDataManagement
import org.bitcoins.dlc.wallet.models.{
DLCSetupDbState,
OfferedDbState,
SetupCompleteDLCDbState
}
import org.bitcoins.keymanager.config.KeyManagerAppConfig
import org.bitcoins.wallet.config.WalletAppConfig
import org.bitcoins.wallet.models.TransactionDAO
import org.bitcoins.wallet.{Wallet, WalletLogger}
import java.nio.file._
@ -63,17 +60,18 @@ case class DLCAppConfig(
migrate()
}
val f = if (initMigrations != 0 && initMigrations <= 5) {
val f = if (initMigrations != 0 && initMigrations <= 9) {
//means we have an old wallet that we need to migrate
logger.info(s"Running serialization version migration code")
serializationVersionMigration()
logger.info(s"Deleting alpha version DLCs")
deleteAlphaVersionDLCs()
} else {
//the wallet is new enough where we cannot have any old
//DLCs in the database with a broken contractId
Future.unit
}
logger.info(s"Applied $numMigrations to the dlc project")
logger.info(
s"Applied ${numMigrations.migrationsExecuted} to the dlc project. Started with initMigrations=$initMigrations")
f
}
@ -127,84 +125,29 @@ case class DLCAppConfig(
callbacks.atomicUpdate(newCallbacks)(_ + _)
}
/** Correctly populates the serialization version for existing DLCs
* in our wallet database
*/
private def serializationVersionMigration(): Future[Unit] = {
/** Delete alpha version DLCs, these are old protocol format DLCs that cannot be safely updated to the new protocol version of DLCs */
private def deleteAlphaVersionDLCs(): Future[Unit] = {
val dlcManagement = DLCDataManagement.fromDbAppConfig()(this, ec)
val dlcDAO = dlcManagement.dlcDAO
//read all existing DLCs
val allDlcsF = dlcDAO.findAll()
//ugh, this is kinda nasty, idk how to make better though
val walletAppConfig =
WalletAppConfig(baseDatadir, configOverrides)
val txDAO: TransactionDAO =
TransactionDAO()(ec = ec, appConfig = walletAppConfig)
//get the offers so we can figure out what the serialization version is
val dlcDbContractInfoOfferF: Future[Vector[DLCSetupDbState]] = {
for {
allDlcs <- allDlcsF
//only DLC with the alpha version need to be migrated
alphaVersionDLCs = allDlcs.filter(
_.serializationVersion == DLCSerializationVersion.Alpha)
nestedOfferAndAccept = alphaVersionDLCs.map { a =>
val setupDbOptF =
dlcManagement.getDLCFundingData(a.dlcId, txDAO = txDAO)
setupDbOptF.foreach {
case Some(_) => //happy path, do nothing
case None =>
logger.warn(s"Corrupted dlcId=${a.dlcId.hex} state=${a.state}, " +
s"this is likely because of issue 4001 https://github.com/bitcoin-s/bitcoin-s/issues/4001 . " +
s"This DLC will not have its contractId migrated to DLSerializationVersion.Beta")
}
setupDbOptF
val alphaDLCsF =
dlcDAO
.findAll()
.map(_.filter(_.serializationVersion == DLCSerializationVersion.Alpha))
for {
alphaDLCs <- alphaDLCsF
_ <- Future.traverse(alphaDLCs) { dlc =>
dlc.state match {
case _: ClosedState | DLCState.Offered | DLCState.Accepted |
_: AdaptorSigComputationState | DLCState.Signed =>
logger.info(
s"Deleting alpha version of a dlcId=${dlc.dlcId.hex} dlc=$dlc")
dlcManagement.deleteByDLCId(dlc.dlcId)
case DLCState.Broadcasted | DLCState.Confirmed =>
sys.error(
s"Cannot upgrade our DLC wallet as we have DLCs in progress using an ancient format of DLCs, dlcId=${dlc.dlcId.hex}")
}
offerAndAccepts <- Future.sequence(nestedOfferAndAccept)
} yield {
offerAndAccepts.flatten
}
}
//now we need to insert the serialization type
//into global_dlc_data
val updatedDLCDbsF = for {
dlcDbContractInfoOffer <- dlcDbContractInfoOfferF
} yield setSerializationVersions(dlcDbContractInfoOffer)
val updatedInDbF = updatedDLCDbsF.flatMap(dlcDAO.updateAll)
updatedInDbF.map(_ => ())
}
/** Sets serialization versions on [[DLCDb]] based on the corresponding [[ContractInfo]] */
private def setSerializationVersions(
vec: Vector[DLCSetupDbState]): Vector[DLCDb] = {
vec.map { case state: DLCSetupDbState =>
val updatedDlcDb: DLCDb = state match {
case acceptDbState: SetupCompleteDLCDbState =>
val offer = acceptDbState.offer
val acceptWithoutSigs = acceptDbState.acceptWithoutSigs
val dlcDb = acceptDbState.dlcDb
val contractId = DLCUtil.calcContractId(offer, acceptWithoutSigs)
logger.info(
s"Updating contractId for dlcId=${dlcDb.dlcId.hex} old contractId=${dlcDb.contractIdOpt
.map(_.toHex)} new contractId=${contractId.toHex}")
dlcDb.copy(tempContractId = offer.tempContractId,
contractIdOpt = Some(contractId),
serializationVersion = DLCSerializationVersion.Beta)
case offerDbState: OfferedDbState =>
//if we don't have an accept message, we can only calculate tempContractId
val dlcDb = offerDbState.dlcDb
val offer = offerDbState.offer
logger.info(
s"Updating tempContractId for dlcId=${dlcDb.dlcId.hex} old tempContractId=${dlcDb.tempContractId.hex} new contractId=${offer.tempContractId.hex}")
dlcDb.copy(tempContractId = offer.tempContractId,
serializationVersion = DLCSerializationVersion.Beta)
}
updatedDlcDb
}
} yield ()
}
}

View file

@ -775,6 +775,11 @@ case class DLCDataManagement(dlcWalletDAOs: DLCWalletDAOs)(implicit
safeDatabase.run(action)
}
def deleteByDLCId(dlcId: Sha256Digest): Future[Unit] = {
val action = actionBuilder.deleteDLCAction(dlcId)
safeDatabase.run(action)
}
/** Retrieves the transaction(s) used to fund the offer message */
private def getOfferPrevTxs(
dlcDb: DLCDb,

View file

@ -173,7 +173,12 @@ case class DLCDAO()(implicit
safeDatabase.run(action).map(_ => ())
}
private def updatePeerAction(dlcId: Sha256Digest, peerOpt: Option[String]) = {
private def updatePeerAction(
dlcId: Sha256Digest,
peerOpt: Option[String]): DBIOAction[
Int,
NoStream,
Effect.Read with Effect.Write] = {
val dlcQuery = table.filter(_.dlcId === dlcId)
for {
@ -186,6 +191,14 @@ case class DLCDAO()(implicit
} yield res
}
def findByDLCSerializationVersion(
version: DLCSerializationVersion): Future[Vector[DLCDb]] = {
val action = table.filter(_.serializationVersion === version).result
safeDatabase
.run(action)
.map(_.toVector)
}
class DLCTable(tag: Tag)
extends Table[DLCDb](tag, schemaName, "global_dlc_data") {