Fix verify funding sigs (#3194)

* Fix verify funding sigs

* Sort funding inputs

* Sort utxos in dlc signer

* Other multi input fixes & tests

* Fix compile error
This commit is contained in:
benthecarman 2021-05-28 17:59:45 -07:00 committed by GitHub
parent 7a5e108ff2
commit 0e701bc9d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 55 additions and 35 deletions

View File

@ -48,10 +48,14 @@ case class DLCTxSigner(
finalAddress == offer.pubKeys.payoutAddress,
"Given keys do not match public key and address in offer")
val fundingUtxosAsInputs =
fundingUtxos.zip(offer.fundingInputs).map { case (utxo, fund) =>
DLCFundingInput.fromInputSigningInfo(utxo, fund.inputSerialId)
}
require(fundingUtxosAsInputs == offer.fundingInputs,
fundingUtxos
.sortBy(_.outPoint.bytes)
.zip(offer.fundingInputs.sortBy(_.outPoint.bytes))
.map { case (utxo, fund) =>
DLCFundingInput.fromInputSigningInfo(utxo, fund.inputSerialId)
}
.sortBy(_.inputSerialId)
require(fundingUtxosAsInputs == offer.fundingInputs.sortBy(_.inputSerialId),
"Funding ScriptSignatureParams did not match offer funding inputs")
} else {
require(
@ -60,11 +64,17 @@ case class DLCTxSigner(
"Given keys do not match public key and address in accept"
)
val fundingUtxosAsInputs =
fundingUtxos.zip(accept.fundingInputs).map { case (utxo, fund) =>
DLCFundingInput.fromInputSigningInfo(utxo, fund.inputSerialId)
}
require(fundingUtxosAsInputs == accept.fundingInputs,
"Funding ScriptSignatureParams did not match accept funding inputs")
fundingUtxos
.sortBy(_.outPoint.bytes)
.zip(accept.fundingInputs.sortBy(_.outPoint.bytes))
.map { case (utxo, fund) =>
DLCFundingInput.fromInputSigningInfo(utxo, fund.inputSerialId)
}
.sortBy(_.inputSerialId)
require(
fundingUtxosAsInputs == accept.fundingInputs.sortBy(_.inputSerialId),
"Funding ScriptSignatureParams did not match accept funding inputs"
)
}
/** Return's this party's payout for a given oracle signature */

View File

@ -155,8 +155,8 @@ object DLCSignatureVerifier {
val psbt = PSBT.fromUnsignedTxWithP2SHScript(fundingTx)
fundingSigs.zipWithIndex
.foldLeft(true) { case (ret, ((outPoint, witness), index)) =>
fundingSigs
.foldLeft(true) { case (ret, (outPoint, witness)) =>
val serialId = serialIdMap(outPoint)
val idx = serialIds.indexOf(serialId)
if (ret) {
@ -167,7 +167,13 @@ object DLCSignatureVerifier {
false
} else {
Try {
val fundingInput = remoteFundingInputs(index)
val fundingInput =
remoteFundingInputs.find(_.outPoint == outPoint) match {
case Some(input) => input
case None =>
throw new RuntimeException(
s"Could not find fundingInput for outpoint $outPoint")
}
psbt
.addUTXOToInput(fundingInput.prevTx, idx)

View File

@ -27,7 +27,7 @@ class DLCMultiOracleEnumExecutionTest extends BitcoinSDualWalletTest {
val outcomes: Vector[String] = DLCTestUtil.genOutcomes(numOutcomes)
val (contractDescriptor, _) =
DLCTestUtil.genContractDescriptors(outcomes, Satoshis(10000))
DLCTestUtil.genContractDescriptors(outcomes, total)
val announcements: Vector[OracleAnnouncementTLV] =
privateKeys.zip(kValues).map { case (priv, kValue) =>

View File

@ -937,7 +937,7 @@ abstract class DLCWallet
def verifyFundingSigs(
inputs: Vector[DLCFundingInputDb],
sign: DLCSign): Future[Boolean] = {
if (inputs.count(!_.isInitiator) == sign.fundingSigs.length) {
if (inputs.count(_.isInitiator) == sign.fundingSigs.length) {
verifierFromDb(sign.contractId).map { verifier =>
verifier.verifyRemoteFundingSigs(sign.fundingSigs)
}
@ -994,16 +994,16 @@ abstract class DLCWallet
contractData <- contractDataDAO.read(dlcDb.dlcId).map(_.get)
offerDbOpt <- dlcOfferDAO.findByDLCId(dlcDb.dlcId)
offerDb = offerDbOpt.get
fundingInputDbs <- dlcInputsDAO.findByDLCId(dlcDb.dlcId)
fundingInputDbs <- dlcInputsDAO.findByDLCId(dlcDb.dlcId,
isInitiator = true)
txIds = fundingInputDbs.map(_.outPoint.txIdBE)
remotePrevTxs <- remoteTxDAO.findByTxIdBEs(txIds)
localPrevTxs <- transactionDAO.findByTxIdBEs(txIds)
(announcements, announcementData, nonceDbs) <- getDLCAnnouncementDbs(
dlcDb.dlcId)
prevTxs = (remotePrevTxs ++ localPrevTxs).map(_.transaction)
prevTxs = remotePrevTxs.map(_.transaction)
txs = prevTxs.groupBy(_.txIdBE)
fundingInputs = fundingInputDbs.map(input =>

View File

@ -139,7 +139,7 @@ private[bitcoins] trait DLCDataManagement { self: DLCWallet =>
EnumMultiOracleInfo(contractDataDb.oracleThreshold,
announcementTLVs)
}
ContractInfo(enum, oracleInfo)
ContractInfo(contractDataDb.totalCollateral.satoshis, enum, oracleInfo)
case numeric: NumericContractDescriptor =>
val oracleInfo =
if (announcementTLVs.size == 1) {
@ -318,7 +318,7 @@ private[bitcoins] trait DLCDataManagement { self: DLCWallet =>
localFundingInputs = fundingInputsDb.filter(_.isInitiator)
prevTxs <-
transactionDAO.findByTxIdBEs(fundingInputsDb.map(_.outPoint.txIdBE))
transactionDAO.findByTxIdBEs(localFundingInputs.map(_.outPoint.txIdBE))
} yield {
val offerFundingInputs =
matchPrevTxsWithInputs(localFundingInputs, prevTxs)
@ -359,12 +359,12 @@ private[bitcoins] trait DLCDataManagement { self: DLCWallet =>
dlcAccept: DLCAcceptDb,
fundingInputsDb: Vector[DLCFundingInputDb],
contractInfo: ContractInfo): Future[DLCTxBuilder] = {
val (offerDbFundingInputs, acceptDbFundingInputs) =
fundingInputsDb.partition(_.isInitiator)
val (localDbFundingInputs, remoteDbFundingInputs) = if (dlcDb.isInitiator) {
(offerDbFundingInputs, acceptDbFundingInputs)
(fundingInputsDb.filter(_.isInitiator),
fundingInputsDb.filterNot(_.isInitiator))
} else {
(acceptDbFundingInputs, offerDbFundingInputs)
(fundingInputsDb.filterNot(_.isInitiator),
fundingInputsDb.filter(_.isInitiator))
}
for {

View File

@ -84,7 +84,8 @@ trait BitcoinSDualWalletTest extends BitcoinSWalletTest {
for {
walletA <- walletAF
walletB <- walletBF
contractInfo = ContractInfo(Satoshis(10000), contractOraclePair)
amt = expectedDefaultAmt / Satoshis(2)
contractInfo = ContractInfo(amt.satoshis, contractOraclePair)
(dlcWalletA, dlcWalletB) <-
DLCWalletUtil.initDLC(walletA, walletB, contractInfo)
} yield (dlcWalletA, dlcWalletB)

View File

@ -22,6 +22,7 @@ import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
import org.bitcoins.crypto._
import org.bitcoins.dlc.wallet.DLCWallet
import org.bitcoins.dlc.wallet.models._
import org.bitcoins.testkit.wallet.BitcoinSWalletTest.expectedDefaultAmt
import org.bitcoins.testkit.wallet.FundWalletUtil.FundedDLCWallet
import org.bitcoins.testkitcore.dlc.DLCTestUtil
import org.scalatest.Assertions.fail
@ -48,8 +49,11 @@ object DLCWalletUtil {
lazy val loseHash: Sha256Digest =
CryptoUtil.sha256DLCAttestation(loseStr)
val total: Satoshis = (expectedDefaultAmt / Satoshis(2)).satoshis
val half: Satoshis = (total / Satoshis(2)).satoshis
val sampleOutcomes: Vector[(EnumOutcome, Satoshis)] = Vector(
EnumOutcome(winStr) -> Satoshis(10000),
EnumOutcome(winStr) -> (expectedDefaultAmt / Satoshis(2)).satoshis,
EnumOutcome(loseStr) -> Satoshis.zero)
lazy val sampleContractDescriptor: EnumContractDescriptor =
@ -64,7 +68,7 @@ object DLCWalletUtil {
ContractOraclePair.EnumPair(sampleContractDescriptor, sampleOracleInfo)
lazy val sampleContractInfo: ContractInfo =
ContractInfo(Satoshis(10000), sampleContractOraclePair)
ContractInfo(half, sampleContractOraclePair)
lazy val sampleOracleWinSig: SchnorrDigitalSignature =
oraclePrivKey.schnorrSignWithNonce(winHash.bytes, kValue)
@ -75,7 +79,7 @@ object DLCWalletUtil {
val numDigits: Int = 6
lazy val multiNonceContractDescriptor: NumericContractDescriptor =
DLCTestUtil.genMultiDigitContractInfo(numDigits, Satoshis(10000))._1
DLCTestUtil.genMultiDigitContractInfo(numDigits, total)._1
lazy val multiNonceOracleInfo: NumericSingleOracleInfo =
NumericSingleOracleInfo(
@ -88,7 +92,7 @@ object DLCWalletUtil {
}
lazy val multiNonceContractInfo: ContractInfo =
ContractInfo(Satoshis(10000), multiNonceContractOraclePair)
ContractInfo(total, multiNonceContractOraclePair)
lazy val dummyContractMaturity: BlockTimeStamp = BlockTimeStamp(1666335)
lazy val dummyContractTimeout: BlockTimeStamp = BlockTimeStamp(1666337)
@ -119,8 +123,7 @@ object DLCWalletUtil {
val dummyPrevTx: BaseTransaction = BaseTransaction(
TransactionConstants.validLockVersion,
Vector.empty,
Vector.fill(2)(
TransactionOutput(Satoshis(5000), P2WPKHWitnessSPKV0(dummyKey))),
Vector.fill(2)(TransactionOutput(half, P2WPKHWitnessSPKV0(dummyKey))),
UInt32.zero)
val dummyFundingInputs = Vector(
@ -143,7 +146,7 @@ object DLCWalletUtil {
lazy val sampleDLCOffer: DLCOffer = DLCOffer(
contractInfo = sampleContractInfo,
pubKeys = dummyDLCKeys,
totalCollateral = Satoshis(5000),
totalCollateral = half,
fundingInputs = Vector(dummyFundingInputs.head),
changeAddress = dummyAddress,
payoutSerialId = sampleOfferPayoutSerialId,
@ -176,7 +179,7 @@ object DLCWalletUtil {
Vector(sampleOfferChangeSerialId, sampleFundOutputSerialId))
lazy val sampleDLCAccept: DLCAccept = DLCAccept(
totalCollateral = Satoshis(5000),
totalCollateral = half,
pubKeys = dummyDLCKeys,
fundingInputs = Vector(dummyFundingInputs.last),
changeAddress = dummyAddress,
@ -220,7 +223,7 @@ object DLCWalletUtil {
contractDescriptorTLV = sampleContractDescriptor.toTLV,
contractMaturity = BlockTimeStamp(0),
contractTimeout = BlockTimeStamp(1),
totalCollateral = Satoshis(10000)
totalCollateral = total
)
def initDLC(
@ -234,8 +237,8 @@ object DLCWalletUtil {
for {
offer <- walletA.createDLCOffer(
contractInfo = contractInfo,
collateral = Satoshis(5000),
feeRateOpt = None,
collateral = half,
feeRateOpt = Some(SatoshisPerVirtualByte.fromLong(10)),
locktime = dummyTimeouts.contractMaturity.toUInt32,
refundLocktime = dummyTimeouts.contractTimeout.toUInt32
)