mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-01-19 05:43:51 +01:00
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:
parent
c92d5760f3
commit
8081772b57
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user