Allow acceptor to upsert same outpoint multiple times for different offers (#4230)

* Allow acceptor to upsert same outpoint multiple times for different offers

* Remove extra log

* Revert DLCOffer invariant

* Fix OP_RETURN tests
This commit is contained in:
Chris Stewart 2022-03-31 07:47:10 -05:00 committed by GitHub
parent c92d5760f3
commit 8081772b57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 72 additions and 13 deletions

View File

@ -128,6 +128,7 @@ object Satoshis
val max = Satoshis(Int64.max)
val zero = Satoshis(Int64.zero)
val one = Satoshis(Int64.one)
val two = Satoshis(2)
override def fromBytes(bytes: ByteVector): Satoshis =
RawSatoshisSerializer.read(bytes)

View File

@ -153,7 +153,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
fundedDLCWallets: (FundedDLCWallet, FundedDLCWallet) =>
// construct a contract info that uses many inputs
val totalCol = Bitcoins(11).satoshis
val col = totalCol / Satoshis(2)
val col = totalCol / Satoshis.two
val outcomes: Vector[(EnumOutcome, Satoshis)] =
Vector(EnumOutcome(winStr) -> totalCol,
@ -897,7 +897,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
def makeOffer(contractInfo: ContractInfoV0TLV): Future[DLCOffer] = {
walletA.createDLCOffer(
contractInfoTLV = contractInfo,
collateral = totalCollateral,
collateral = (totalCollateral / Satoshis.two).satoshis,
feeRateOpt = feeRateOpt,
locktime = UInt32.zero,
refundLT = UInt32.one,
@ -928,7 +928,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
def makeOffer(contractInfo: ContractInfoV0TLV): Future[DLCOffer] = {
walletA.createDLCOffer(
contractInfoTLV = contractInfo,
collateral = totalCollateral,
collateral = (totalCollateral / Satoshis.two).satoshis,
feeRateOpt = feeRateOpt,
locktime = UInt32.zero,
refundLT = UInt32.one,
@ -1032,7 +1032,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
val feeRateOpt = Some(SatoshisPerVirtualByte(Satoshis.one))
val totalCollateral = Satoshis(5000)
val feeRateOpt1 = Some(SatoshisPerVirtualByte(Satoshis(2)))
val feeRateOpt1 = Some(SatoshisPerVirtualByte(Satoshis.two))
val totalCollateral1 = Satoshis(10000)
// random testnet addresses
@ -1080,4 +1080,41 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
}
}
it must "setup a DLC and allow re-use of inputs on the accept side" in {
FundedDLCWallets: (FundedDLCWallet, FundedDLCWallet) =>
val walletA = FundedDLCWallets._1.wallet
val walletB = FundedDLCWallets._2.wallet
val offerData: DLCOffer =
DLCWalletUtil.sampleDLCOffer.copy(contractInfo =
DLCWalletUtil.sampleContractInfo2,
totalCollateral = DLCWalletUtil.amt2)
val offerData2 = DLCWalletUtil.sampleDLCOffer
for {
offer1 <- walletA.createDLCOffer(
offerData.contractInfo,
offerData.totalCollateral,
Some(offerData.feeRate),
offerData.timeouts.contractMaturity.toUInt32,
offerData.timeouts.contractTimeout.toUInt32,
None,
None
)
//accept it for the first time using the inputs
_ <- walletB.acceptDLCOffer(offer1.toTLV, None, None)
//cancel the offer
_ <- walletA.cancelDLC(dlcId = offer1.dlcId)
amt = DLCWalletUtil.half
offer2 <- walletA.createDLCOffer(
offerData2.contractInfo,
amt,
Some(offerData2.feeRate),
offerData2.timeouts.contractMaturity.toUInt32,
offerData2.timeouts.contractTimeout.toUInt32,
None,
None
)
_ <- walletB.acceptDLCOffer(offer2.toTLV, None, None)
} yield succeed
}
}

View File

@ -652,7 +652,6 @@ abstract class DLCWallet
val dlcId = calcDLCId(offer.fundingInputs.map(_.outPoint))
val collateral = offer.contractInfo.max - offer.totalCollateral
logger.debug(s"Checking if Accept (${dlcId.hex}) has already been made")
for {
dlcAcceptOpt <- DLCAcceptUtil.findDLCAccept(dlcId = dlcId,
@ -721,7 +720,6 @@ abstract class DLCWallet
externalChangeAddressOpt: Option[BitcoinAddress]): Future[DLCAccept] =
Future {
DLCWallet.AcceptingOffersLatch.startAccepting(offer.tempContractId)
logger.info(
s"Creating DLC Accept for tempContractId ${offer.tempContractId.hex}")
val result = for {
@ -740,7 +738,10 @@ abstract class DLCWallet
externalPayoutAddressOpt = externalPayoutAddressOpt,
externalChangeAddressOpt = externalChangeAddressOpt
)
_ = require(
initializedAccept.acceptWithoutSigs.tempContractId == offer.tempContractId,
s"Offer and Accept have differing tempContractIds! offer=${offer.tempContractId} accept=${initializedAccept.acceptWithoutSigs.tempContractId}"
)
offerPrevTxs = offer.fundingInputs.map(funding =>
TransactionDbHelper.fromTransaction(funding.prevTx,
blockHashOpt = None))
@ -817,9 +818,6 @@ abstract class DLCWallet
refundSig = refundSig)
.copy(isExternalAddress = status.payoutAddress.forall(_.isExternal))
_ = require(accept.tempContractId == offer.tempContractId,
"Offer and Accept have differing tempContractIds!")
actions = actionBuilder.buildCreateAcceptAction(
dlcDb = dlc.updateState(DLCState.Accepted),
offerInputs = offerInputs,

View File

@ -61,9 +61,9 @@ case class DLCActionBuilder(dlcWalletDAOs: DLCWalletDAOs) {
refundSigsDb: DLCRefundSigsDb)(implicit ec: ExecutionContext): DBIOAction[
Unit,
NoStream,
Effect.Write with Effect.Transactional] = {
Effect.Write with Effect.Read with Effect.Transactional] = {
val dlcDbAction = dlcDAO.updateAction(dlcDb)
val inputAction = dlcInputsDAO.createAllAction(offerInputs)
val inputAction = dlcInputsDAO.upsertAllAction(offerInputs)
val sigsAction = dlcSigsDAO.createAllAction(cetSigsDb)
val refundSigAction = dlcRefundSigDAO.createAction(refundSigsDb)
val actions = Vector(dlcDbAction, inputAction, sigsAction, refundSigAction)

View File

@ -86,6 +86,11 @@ object DLCWalletUtil extends Logging {
lazy val sampleContractInfo: ContractInfo =
SingleContractInfo(half, sampleContractOraclePair)
val amt2: Satoshis = Satoshis(100000)
lazy val sampleContractInfo2: ContractInfo =
SingleContractInfo(amt2, sampleContractOraclePair)
lazy val invalidContractInfo: ContractInfo =
SingleContractInfo(half, invalidContractOraclePair)
@ -180,6 +185,20 @@ object DLCWalletUtil extends Logging {
timeouts = dummyTimeouts
)
lazy val sampleDLCOffer2 = DLCOffer(
protocolVersionOpt = DLCOfferTLV.currentVersionOpt,
contractInfo = sampleContractInfo2,
pubKeys = dummyDLCKeys,
totalCollateral = sampleContractInfo2.totalCollateral,
fundingInputs = Vector(dummyFundingInputs.head),
changeAddress = dummyAddress,
payoutSerialId = sampleOfferPayoutSerialId,
changeSerialId = sampleOfferChangeSerialId,
fundOutputSerialId = sampleFundOutputSerialId,
feeRate = SatoshisPerVirtualByte(Satoshis(3)),
timeouts = dummyTimeouts
)
lazy val invalidDLCOffer: DLCOffer = DLCOffer(
protocolVersionOpt = DLCOfferTLV.currentVersionOpt,
contractInfo = invalidContractInfo,

View File

@ -57,7 +57,11 @@ trait FundTransactionHandling extends WalletLogger { self: Wallet =>
markAsReserved: Boolean): Future[(
RawTxBuilderWithFinalizer[ShufflingNonInteractiveFinalizer],
Vector[ScriptSignatureParams[InputInfo]])] = {
val amt = destinations.map(_.value).sum
val amts = destinations.map(_.value)
//need to allow 0 for OP_RETURN outputs
require(amts.forall(_.satoshis.toBigInt >= 0),
s"Cannot fund a transaction for a negative amount, got=$amts")
val amt = amts.sum
logger.info(s"Attempting to fund a tx for amt=${amt} with feeRate=$feeRate")
val utxosF: Future[Vector[(SpendingInfoDb, Transaction)]] =
for {