mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-03 18:47:38 +01:00
2022 02 03 issue 4032 (#4042)
* get all dlcWalletTest/test passing * Get dlcTest/test working with ignored test cases * WIP * Rework match oracleSignatures * Refactor checkSingleContractInfoOracleSigs to use matchOracleSignatures * Clean up checkOracleSignaturesAgainstContract for disjoint, still doesn't pass test cases * Some DRY in numeric test cases * Add test case for enum contracts * Fix disjoint union contract bug * Refactor matching of oracle announcements and oracle signatures into DLCUtil * Fix compile on on 2.12.x * Address parts of code review
This commit is contained in:
parent
142612f034
commit
7a6f0430d6
9 changed files with 253 additions and 112 deletions
|
@ -749,7 +749,7 @@ lazy val dlcWalletTest = project
|
||||||
name := "bitcoin-s-dlc-wallet-test",
|
name := "bitcoin-s-dlc-wallet-test",
|
||||||
libraryDependencies ++= Deps.dlcWalletTest
|
libraryDependencies ++= Deps.dlcWalletTest
|
||||||
)
|
)
|
||||||
.dependsOn(coreJVM % testAndCompile, dlcWallet, testkit, dlcTest)
|
.dependsOn(coreJVM % testAndCompile, dlcWallet, testkit, testkitCoreJVM, dlcTest)
|
||||||
|
|
||||||
lazy val dlcNode = project
|
lazy val dlcNode = project
|
||||||
.in(file("dlc-node"))
|
.in(file("dlc-node"))
|
||||||
|
|
|
@ -8,9 +8,14 @@ import org.bitcoins.core.protocol.dlc.models.DLCMessage.{
|
||||||
DLCAcceptWithoutSigs,
|
DLCAcceptWithoutSigs,
|
||||||
DLCOffer
|
DLCOffer
|
||||||
}
|
}
|
||||||
import org.bitcoins.core.protocol.dlc.models.{ContractInfo, OracleOutcome}
|
import org.bitcoins.core.protocol.dlc.models._
|
||||||
import org.bitcoins.core.protocol.script.P2WSHWitnessV0
|
import org.bitcoins.core.protocol.script.P2WSHWitnessV0
|
||||||
|
import org.bitcoins.core.protocol.tlv.{
|
||||||
|
OracleAnnouncementTLV,
|
||||||
|
OracleAttestmentTLV
|
||||||
|
}
|
||||||
import org.bitcoins.core.protocol.transaction.{Transaction, WitnessTransaction}
|
import org.bitcoins.core.protocol.transaction.{Transaction, WitnessTransaction}
|
||||||
|
import org.bitcoins.core.util.sorted.OrderedAnnouncements
|
||||||
import org.bitcoins.crypto._
|
import org.bitcoins.crypto._
|
||||||
import scodec.bits.ByteVector
|
import scodec.bits.ByteVector
|
||||||
|
|
||||||
|
@ -187,4 +192,98 @@ object DLCUtil {
|
||||||
outputIdx = fundingOutputIdx,
|
outputIdx = fundingOutputIdx,
|
||||||
tempContractId = offer.tempContractId)
|
tempContractId = offer.tempContractId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Checks that the oracles signatures given to us are correct
|
||||||
|
* Things we need to check
|
||||||
|
* 1. We have all the oracle signatures
|
||||||
|
* 2. The oracle signatures are for one of the contracts in the [[ContractInfo]]
|
||||||
|
* @see https://github.com/bitcoin-s/bitcoin-s/issues/4032
|
||||||
|
*/
|
||||||
|
def checkOracleSignaturesAgainstContract(
|
||||||
|
contractInfo: ContractInfo,
|
||||||
|
oracleSigs: Vector[OracleSignatures]): Boolean = {
|
||||||
|
contractInfo match {
|
||||||
|
case single: SingleContractInfo =>
|
||||||
|
checkSingleContractInfoOracleSigs(single, oracleSigs)
|
||||||
|
case disjoint: DisjointUnionContractInfo =>
|
||||||
|
//at least one disjoint union contract
|
||||||
|
//has to have matching signatures
|
||||||
|
disjoint.contracts.exists { single: SingleContractInfo =>
|
||||||
|
checkSingleContractInfoOracleSigs(single, oracleSigs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check if the given [[SingleContractInfo]] has one [[OracleSignatures]]
|
||||||
|
* matches it inside of oracleSignatures.
|
||||||
|
*/
|
||||||
|
private def checkSingleContractInfoOracleSigs(
|
||||||
|
contractInfo: SingleContractInfo,
|
||||||
|
oracleSignatures: Vector[OracleSignatures]): Boolean = {
|
||||||
|
require(oracleSignatures.nonEmpty, s"Signatures cannot be empty")
|
||||||
|
matchOracleSignatures(contractInfo, oracleSignatures).isDefined
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Matches a [[SingleContractInfo]] to its oracle's signatures */
|
||||||
|
def matchOracleSignatures(
|
||||||
|
contractInfo: SingleContractInfo,
|
||||||
|
oracleSignatures: Vector[OracleSignatures]): Option[OracleSignatures] = {
|
||||||
|
matchOracleSignatures(contractInfo.announcements, oracleSignatures)
|
||||||
|
}
|
||||||
|
|
||||||
|
def matchOracleSignatures(
|
||||||
|
announcements: Vector[OracleAnnouncementTLV],
|
||||||
|
oracleSignatures: Vector[OracleSignatures]): Option[OracleSignatures] = {
|
||||||
|
val announcementNonces: Vector[Vector[SchnorrNonce]] = {
|
||||||
|
announcements
|
||||||
|
.map(_.eventTLV.nonces)
|
||||||
|
.map(_.vec)
|
||||||
|
}
|
||||||
|
val resultOpt = oracleSignatures.find { case oracleSignature =>
|
||||||
|
val oracleSigNonces: Vector[SchnorrNonce] = oracleSignature.sigs.map(_.rx)
|
||||||
|
announcementNonces.contains(oracleSigNonces)
|
||||||
|
}
|
||||||
|
resultOpt
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Checks to see if the given oracle signatures and announcement have the same nonces */
|
||||||
|
private def matchOracleSignaturesForAnnouncements(
|
||||||
|
announcement: OracleAnnouncementTLV,
|
||||||
|
signature: OracleSignatures): Option[OracleSignatures] = {
|
||||||
|
matchOracleSignatures(
|
||||||
|
Vector(announcement),
|
||||||
|
Vector(signature)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Builds a set of oracle signatures from given announcements
|
||||||
|
* and attestations. This method discards attestments
|
||||||
|
* that do not have a matching announcement. Those attestments
|
||||||
|
* are not included in the returned set of [[OracleSignatures]]
|
||||||
|
*/
|
||||||
|
def buildOracleSignatures(
|
||||||
|
announcements: OrderedAnnouncements,
|
||||||
|
attestments: Vector[OracleAttestmentTLV]): Vector[OracleSignatures] = {
|
||||||
|
val result: Vector[OracleSignatures] = {
|
||||||
|
val init = Vector.empty[OracleSignatures]
|
||||||
|
attestments
|
||||||
|
.foldLeft(init) { (acc, attestment) =>
|
||||||
|
val r: Vector[OracleSignatures] = announcements.flatMap { ann =>
|
||||||
|
val oracleSig =
|
||||||
|
OracleSignatures(SingleOracleInfo(ann), attestment.sigs)
|
||||||
|
val isMatch = matchOracleSignaturesForAnnouncements(ann, oracleSig)
|
||||||
|
isMatch match {
|
||||||
|
case Some(matchedSig) =>
|
||||||
|
acc.:+(matchedSig)
|
||||||
|
case None =>
|
||||||
|
//don't add it, skip it
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
}.toVector
|
||||||
|
r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package org.bitcoins.core.protocol.dlc.execution
|
||||||
|
|
||||||
import org.bitcoins.core.currency.CurrencyUnit
|
import org.bitcoins.core.currency.CurrencyUnit
|
||||||
import org.bitcoins.core.protocol.dlc.build.DLCTxBuilder
|
import org.bitcoins.core.protocol.dlc.build.DLCTxBuilder
|
||||||
import org.bitcoins.core.protocol.dlc.compute.CETCalculator
|
import org.bitcoins.core.protocol.dlc.compute.{CETCalculator, DLCUtil}
|
||||||
import org.bitcoins.core.protocol.dlc.models._
|
import org.bitcoins.core.protocol.dlc.models._
|
||||||
import org.bitcoins.core.protocol.dlc.sign.DLCTxSigner
|
import org.bitcoins.core.protocol.dlc.sign.DLCTxSigner
|
||||||
import org.bitcoins.core.protocol.transaction.{Transaction, WitnessTransaction}
|
import org.bitcoins.core.protocol.transaction.{Transaction, WitnessTransaction}
|
||||||
|
@ -136,6 +136,9 @@ object DLCExecutor {
|
||||||
fundingTx: Transaction,
|
fundingTx: Transaction,
|
||||||
fundOutputIndex: Int
|
fundOutputIndex: Int
|
||||||
): ExecutedDLCOutcome = {
|
): ExecutedDLCOutcome = {
|
||||||
|
require(
|
||||||
|
DLCUtil.checkOracleSignaturesAgainstContract(contractInfo, oracleSigs),
|
||||||
|
s"Incorrect oracle signatures and contract combination")
|
||||||
val sigOracles = oracleSigs.map(_.oracle)
|
val sigOracles = oracleSigs.map(_.oracle)
|
||||||
|
|
||||||
val oracleInfoOpt = contractInfo.oracleInfos.find { oracleInfo =>
|
val oracleInfoOpt = contractInfo.oracleInfos.find { oracleInfo =>
|
||||||
|
|
|
@ -3,16 +3,16 @@ package org.bitcoins.dlc.wallet
|
||||||
import org.bitcoins.core.currency.Satoshis
|
import org.bitcoins.core.currency.Satoshis
|
||||||
import org.bitcoins.core.number.UInt32
|
import org.bitcoins.core.number.UInt32
|
||||||
import org.bitcoins.core.protocol.dlc.models.DLCMessage.DLCOffer
|
import org.bitcoins.core.protocol.dlc.models.DLCMessage.DLCOffer
|
||||||
import org.bitcoins.core.protocol.dlc.models.{
|
|
||||||
DLCState,
|
|
||||||
DisjointUnionContractInfo,
|
|
||||||
SingleContractInfo
|
|
||||||
}
|
|
||||||
import org.bitcoins.core.protocol.dlc.models.DLCStatus.{
|
import org.bitcoins.core.protocol.dlc.models.DLCStatus.{
|
||||||
Claimed,
|
Claimed,
|
||||||
Refunded,
|
Refunded,
|
||||||
RemoteClaimed
|
RemoteClaimed
|
||||||
}
|
}
|
||||||
|
import org.bitcoins.core.protocol.dlc.models.{
|
||||||
|
DLCState,
|
||||||
|
DisjointUnionContractInfo,
|
||||||
|
SingleContractInfo
|
||||||
|
}
|
||||||
import org.bitcoins.core.protocol.tlv._
|
import org.bitcoins.core.protocol.tlv._
|
||||||
import org.bitcoins.core.script.interpreter.ScriptInterpreter
|
import org.bitcoins.core.script.interpreter.ScriptInterpreter
|
||||||
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
|
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
|
||||||
|
@ -416,4 +416,41 @@ class DLCExecutionTest extends BitcoinSDualWalletTest {
|
||||||
_ <- walletA.listDLCs()
|
_ <- walletA.listDLCs()
|
||||||
} yield succeed
|
} yield succeed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it must "throw an exception for a enum contract when do not have all the oracle signatures/outcomes" in {
|
||||||
|
wallets =>
|
||||||
|
val walletA = wallets._1.wallet
|
||||||
|
val resultF = for {
|
||||||
|
contractId <- getContractId(walletA)
|
||||||
|
status <- getDLCStatus(walletA)
|
||||||
|
(goodAttestment, _) = {
|
||||||
|
status.contractInfo match {
|
||||||
|
case single: SingleContractInfo =>
|
||||||
|
DLCWalletUtil.getSigs(single)
|
||||||
|
case disjoint: DisjointUnionContractInfo =>
|
||||||
|
sys.error(
|
||||||
|
s"Cannot retrieve sigs for disjoint union contract, got=$disjoint")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//purposefully drop these
|
||||||
|
//we cannot drop just a sig, or just an outcome because
|
||||||
|
//of invariants in OracleAttestmentV0TLV
|
||||||
|
badSigs = goodAttestment.sigs.dropRight(1)
|
||||||
|
badOutcomes = goodAttestment.outcomes.dropRight(1)
|
||||||
|
badAttestment = OracleAttestmentV0TLV(eventId = goodAttestment.eventId,
|
||||||
|
publicKey =
|
||||||
|
goodAttestment.publicKey,
|
||||||
|
sigs = badSigs,
|
||||||
|
outcomes = badOutcomes)
|
||||||
|
func = (wallet: DLCWallet) =>
|
||||||
|
wallet.executeDLC(contractId, badAttestment)
|
||||||
|
|
||||||
|
result <- dlcExecutionTest(wallets = wallets,
|
||||||
|
asInitiator = true,
|
||||||
|
func = func,
|
||||||
|
expectedOutputs = 1)
|
||||||
|
} yield assert(result)
|
||||||
|
|
||||||
|
recoverToSucceededIf[IllegalArgumentException](resultF)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,27 +74,7 @@ class DLCMultiOracleExactNumericExecutionTest extends BitcoinSDualWalletTest {
|
||||||
initiatorWinVec,
|
initiatorWinVec,
|
||||||
None)
|
None)
|
||||||
|
|
||||||
val initiatorWinSigs =
|
val initiatorWinSigs = buildAttestments(initWinOutcomes)
|
||||||
privateKeys.zip(kValues).flatMap { case (priv, kValues) =>
|
|
||||||
val outcomeOpt = initWinOutcomes.oraclesAndOutcomes.find(
|
|
||||||
_._1.publicKey == priv.schnorrPublicKey)
|
|
||||||
|
|
||||||
outcomeOpt.map { case (oracleInfo, outcome) =>
|
|
||||||
val sigs = outcome.digits.zip(kValues).map { case (num, kValue) =>
|
|
||||||
val hash = CryptoUtil.sha256DLCAttestation(num.toString).bytes
|
|
||||||
priv.schnorrSignWithNonce(hash, kValue)
|
|
||||||
}
|
|
||||||
val eventId = oracleInfo.announcement.eventTLV match {
|
|
||||||
case v0: OracleEventV0TLV => v0.eventId
|
|
||||||
}
|
|
||||||
|
|
||||||
OracleAttestmentV0TLV(eventId,
|
|
||||||
priv.schnorrPublicKey,
|
|
||||||
sigs,
|
|
||||||
outcome.digits.map(_.toString))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val recipientChosenOracles =
|
val recipientChosenOracles =
|
||||||
Random.shuffle(oracleIndices).take(oracleInfo.threshold).sorted
|
Random.shuffle(oracleIndices).take(oracleInfo.threshold).sorted
|
||||||
|
|
||||||
|
@ -114,26 +94,7 @@ class DLCMultiOracleExactNumericExecutionTest extends BitcoinSDualWalletTest {
|
||||||
recipientWinVec,
|
recipientWinVec,
|
||||||
None)
|
None)
|
||||||
|
|
||||||
val recipientWinSigs =
|
val recipientWinSigs = buildAttestments(recipientWinOutcomes)
|
||||||
privateKeys.zip(kValues).flatMap { case (priv, kValues) =>
|
|
||||||
val outcomeOpt = recipientWinOutcomes.oraclesAndOutcomes.find(
|
|
||||||
_._1.publicKey == priv.schnorrPublicKey)
|
|
||||||
|
|
||||||
outcomeOpt.map { case (oracleInfo, outcome) =>
|
|
||||||
val sigs = outcome.digits.zip(kValues).map { case (num, kValue) =>
|
|
||||||
val hash = CryptoUtil.sha256DLCAttestation(num.toString).bytes
|
|
||||||
priv.schnorrSignWithNonce(hash, kValue)
|
|
||||||
}
|
|
||||||
val eventId = oracleInfo.announcement.eventTLV match {
|
|
||||||
case v0: OracleEventV0TLV => v0.eventId
|
|
||||||
}
|
|
||||||
|
|
||||||
OracleAttestmentV0TLV(eventId,
|
|
||||||
priv.schnorrPublicKey,
|
|
||||||
sigs,
|
|
||||||
outcome.digits.map(_.toString))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shuffle to make sure ordering doesn't matter
|
// Shuffle to make sure ordering doesn't matter
|
||||||
(Random.shuffle(initiatorWinSigs), Random.shuffle(recipientWinSigs))
|
(Random.shuffle(initiatorWinSigs), Random.shuffle(recipientWinSigs))
|
||||||
|
@ -242,4 +203,32 @@ class DLCMultiOracleExactNumericExecutionTest extends BitcoinSDualWalletTest {
|
||||||
aggregateSignature == statusB.oracleSig
|
aggregateSignature == statusB.oracleSig
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Builds an attestment for the given numeric oracle outcome */
|
||||||
|
private def buildAttestments(
|
||||||
|
outcome: NumericOracleOutcome): Vector[OracleAttestmentTLV] = {
|
||||||
|
privateKeys.zip(kValues).flatMap { case (priv, kValues) =>
|
||||||
|
val outcomeOpt =
|
||||||
|
outcome.oraclesAndOutcomes.find(_._1.publicKey == priv.schnorrPublicKey)
|
||||||
|
|
||||||
|
outcomeOpt.map { case (oracleInfo, outcome) =>
|
||||||
|
val neededPadding = numDigits - outcome.digits.length
|
||||||
|
val digitsPadded = outcome.digits ++ Vector.fill(neededPadding)(0)
|
||||||
|
val sigs = digitsPadded.zip(kValues).map { case (num, kValue) =>
|
||||||
|
val hash = CryptoUtil.sha256DLCAttestation(num.toString).bytes
|
||||||
|
priv.schnorrSignWithNonce(hash, kValue)
|
||||||
|
}
|
||||||
|
val eventId = oracleInfo.announcement.eventTLV match {
|
||||||
|
case v0: OracleEventV0TLV => v0.eventId
|
||||||
|
}
|
||||||
|
|
||||||
|
require(kValues.length == sigs.length,
|
||||||
|
s"kValues.length=${kValues.length} sigs.length=${sigs.length}")
|
||||||
|
OracleAttestmentV0TLV(eventId,
|
||||||
|
priv.schnorrPublicKey,
|
||||||
|
sigs,
|
||||||
|
digitsPadded.map(_.toString))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,26 +81,7 @@ class DLCMultiOracleNumericExecutionTest
|
||||||
initiatorWinVec,
|
initiatorWinVec,
|
||||||
Some(params))
|
Some(params))
|
||||||
|
|
||||||
val initiatorWinSigs =
|
val initiatorWinSigs = buildAttestments(initWinOutcomes)
|
||||||
privateKeys.zip(kValues).flatMap { case (priv, kValues) =>
|
|
||||||
val outcomeOpt = initWinOutcomes.oraclesAndOutcomes.find(
|
|
||||||
_._1.publicKey == priv.schnorrPublicKey)
|
|
||||||
|
|
||||||
outcomeOpt.map { case (oracleInfo, outcome) =>
|
|
||||||
val sigs = outcome.digits.zip(kValues).map { case (num, kValue) =>
|
|
||||||
val hash = CryptoUtil.sha256DLCAttestation(num.toString).bytes
|
|
||||||
priv.schnorrSignWithNonce(hash, kValue)
|
|
||||||
}
|
|
||||||
val eventId = oracleInfo.announcement.eventTLV match {
|
|
||||||
case v0: OracleEventV0TLV => v0.eventId
|
|
||||||
}
|
|
||||||
|
|
||||||
OracleAttestmentV0TLV(eventId,
|
|
||||||
priv.schnorrPublicKey,
|
|
||||||
sigs,
|
|
||||||
outcome.digits.map(_.toString))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val recipientChosenOracles =
|
val recipientChosenOracles =
|
||||||
Random.shuffle(oracleIndices).take(oracleInfo.threshold).sorted
|
Random.shuffle(oracleIndices).take(oracleInfo.threshold).sorted
|
||||||
|
@ -121,26 +102,8 @@ class DLCMultiOracleNumericExecutionTest
|
||||||
recipientWinVec,
|
recipientWinVec,
|
||||||
Some(params))
|
Some(params))
|
||||||
|
|
||||||
val recipientWinSigs =
|
val recipientWinSigs: Vector[OracleAttestmentTLV] = buildAttestments(
|
||||||
privateKeys.zip(kValues).flatMap { case (priv, kValues) =>
|
recipientWinOutcomes)
|
||||||
val outcomeOpt = recipientWinOutcomes.oraclesAndOutcomes.find(
|
|
||||||
_._1.publicKey == priv.schnorrPublicKey)
|
|
||||||
|
|
||||||
outcomeOpt.map { case (oracleInfo, outcome) =>
|
|
||||||
val sigs = outcome.digits.zip(kValues).map { case (num, kValue) =>
|
|
||||||
val hash = CryptoUtil.sha256DLCAttestation(num.toString).bytes
|
|
||||||
priv.schnorrSignWithNonce(hash, kValue)
|
|
||||||
}
|
|
||||||
val eventId = oracleInfo.announcement.eventTLV match {
|
|
||||||
case v0: OracleEventV0TLV => v0.eventId
|
|
||||||
}
|
|
||||||
|
|
||||||
OracleAttestmentV0TLV(eventId,
|
|
||||||
priv.schnorrPublicKey,
|
|
||||||
sigs,
|
|
||||||
outcome.digits.map(_.toString))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shuffle to make sure ordering doesn't matter
|
// Shuffle to make sure ordering doesn't matter
|
||||||
(Random.shuffle(initiatorWinSigs), Random.shuffle(recipientWinSigs))
|
(Random.shuffle(initiatorWinSigs), Random.shuffle(recipientWinSigs))
|
||||||
|
@ -249,4 +212,32 @@ class DLCMultiOracleNumericExecutionTest
|
||||||
aggregateSignature == statusB.oracleSig
|
aggregateSignature == statusB.oracleSig
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Builds an attestment for the given numeric oracle outcome */
|
||||||
|
private def buildAttestments(
|
||||||
|
outcome: NumericOracleOutcome): Vector[OracleAttestmentTLV] = {
|
||||||
|
privateKeys.zip(kValues).flatMap { case (priv, kValues) =>
|
||||||
|
val outcomeOpt =
|
||||||
|
outcome.oraclesAndOutcomes.find(_._1.publicKey == priv.schnorrPublicKey)
|
||||||
|
|
||||||
|
outcomeOpt.map { case (oracleInfo, outcome) =>
|
||||||
|
val neededPadding = numDigits - outcome.digits.length
|
||||||
|
val digitsPadded = outcome.digits ++ Vector.fill(neededPadding)(0)
|
||||||
|
val sigs = digitsPadded.zip(kValues).map { case (num, kValue) =>
|
||||||
|
val hash = CryptoUtil.sha256DLCAttestation(num.toString).bytes
|
||||||
|
priv.schnorrSignWithNonce(hash, kValue)
|
||||||
|
}
|
||||||
|
val eventId = oracleInfo.announcement.eventTLV match {
|
||||||
|
case v0: OracleEventV0TLV => v0.eventId
|
||||||
|
}
|
||||||
|
|
||||||
|
require(kValues.length == sigs.length,
|
||||||
|
s"kValues.length=${kValues.length} sigs.length=${sigs.length}")
|
||||||
|
OracleAttestmentV0TLV(eventId,
|
||||||
|
priv.schnorrPublicKey,
|
||||||
|
sigs,
|
||||||
|
digitsPadded.map(_.toString))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,6 +164,34 @@ class DLCNumericExecutionTest extends BitcoinSDualWalletTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it must "throw an exception for a numeric contract when do not have all the oracle signatures/outcomes" in {
|
||||||
|
wallets =>
|
||||||
|
val resultF = for {
|
||||||
|
contractId <- getContractId(wallets._1.wallet)
|
||||||
|
status <- getDLCStatus(wallets._2.wallet)
|
||||||
|
(_, goodAttestment) = getSigs(status.contractInfo)
|
||||||
|
//purposefully drop these
|
||||||
|
//we cannot drop just a sig, or just an outcome because
|
||||||
|
//of invariants in OracleAttestmentV0TLV
|
||||||
|
badSigs = goodAttestment.sigs.dropRight(1)
|
||||||
|
badOutcomes = goodAttestment.outcomes.dropRight(1)
|
||||||
|
badAttestment = OracleAttestmentV0TLV(eventId = goodAttestment.eventId,
|
||||||
|
publicKey =
|
||||||
|
goodAttestment.publicKey,
|
||||||
|
sigs = badSigs,
|
||||||
|
outcomes = badOutcomes)
|
||||||
|
func = (wallet: DLCWallet) =>
|
||||||
|
wallet.executeDLC(contractId, badAttestment)
|
||||||
|
|
||||||
|
result <- dlcExecutionTest(wallets = wallets,
|
||||||
|
asInitiator = false,
|
||||||
|
func = func,
|
||||||
|
expectedOutputs = 1)
|
||||||
|
} yield assert(result)
|
||||||
|
|
||||||
|
recoverToSucceededIf[IllegalArgumentException](resultF)
|
||||||
|
}
|
||||||
|
|
||||||
private def verifyingMatchingOracleSigs(
|
private def verifyingMatchingOracleSigs(
|
||||||
statusA: Claimed,
|
statusA: Claimed,
|
||||||
statusB: RemoteClaimed): Boolean = {
|
statusB: RemoteClaimed): Boolean = {
|
||||||
|
|
|
@ -1355,21 +1355,9 @@ abstract class DLCWallet
|
||||||
announcementData,
|
announcementData,
|
||||||
nonceDbs)
|
nonceDbs)
|
||||||
|
|
||||||
oracleSigs =
|
oracleSigs = DLCUtil.buildOracleSignatures(announcements =
|
||||||
sigs.foldLeft(Vector.empty[OracleSignatures]) { (acc, sig) =>
|
announcementTLVs,
|
||||||
// Nonces should be unique so searching for the first nonce should be safe
|
attestments = sigs.toVector)
|
||||||
val firstNonce = sig.sigs.head.rx
|
|
||||||
announcementTLVs
|
|
||||||
.find(
|
|
||||||
_.eventTLV.nonces.headOption
|
|
||||||
.contains(firstNonce)) match {
|
|
||||||
case Some(announcement) =>
|
|
||||||
acc :+ OracleSignatures(SingleOracleInfo(announcement), sig.sigs)
|
|
||||||
case None =>
|
|
||||||
throw new RuntimeException(
|
|
||||||
s"Cannot find announcement for associated public key, ${sig.publicKey.hex}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tx <- executeDLC(contractId, oracleSigs)
|
tx <- executeDLC(contractId, oracleSigs)
|
||||||
} yield tx
|
} yield tx
|
||||||
|
|
|
@ -880,8 +880,11 @@ trait DLCTest {
|
||||||
.get
|
.get
|
||||||
._2
|
._2
|
||||||
|
|
||||||
|
val neededPadding =
|
||||||
|
singleOracleInfo.announcement.eventTLV.eventDescriptor.noncesNeeded - digitsToSign.digits.length
|
||||||
|
val paddedDigits = digitsToSign.digits ++ Vector.fill(neededPadding)(0)
|
||||||
val sigs =
|
val sigs =
|
||||||
computeNumericOracleSignatures(digitsToSign.digits,
|
computeNumericOracleSignatures(paddedDigits,
|
||||||
oraclePrivKeys(index),
|
oraclePrivKeys(index),
|
||||||
preCommittedKsPerOracle(index))
|
preCommittedKsPerOracle(index))
|
||||||
|
|
||||||
|
@ -1044,7 +1047,8 @@ trait DLCTest {
|
||||||
outcomeIndices: Vector[Long],
|
outcomeIndices: Vector[Long],
|
||||||
contractParams: ContractParams)(implicit
|
contractParams: ContractParams)(implicit
|
||||||
ec: ExecutionContext): Future[Assertion] = {
|
ec: ExecutionContext): Future[Assertion] = {
|
||||||
executeForCasesInUnion(outcomeIndices.map((0, _)), contractParams)
|
executeForCasesInUnion(outcomeIndices = outcomeIndices.map((0, _)),
|
||||||
|
contractParams = contractParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
def executeForCasesInUnion(
|
def executeForCasesInUnion(
|
||||||
|
@ -1110,12 +1114,14 @@ trait DLCTest {
|
||||||
(numDigits, true, paramsOpt)
|
(numDigits, true, paramsOpt)
|
||||||
}
|
}
|
||||||
|
|
||||||
val oracleSigs = genOracleSignatures(numOutcomes,
|
val oracleSigs = genOracleSignatures(
|
||||||
isMultiDigit,
|
numOutcomesOrDigits = numOutcomes,
|
||||||
singleContractInfo,
|
isNumeric = isMultiDigit,
|
||||||
possibleOutcomesForContract,
|
contractInfo = singleContractInfo,
|
||||||
outcomeIndex,
|
outcomes = possibleOutcomesForContract,
|
||||||
paramsOpt)
|
outcomeIndex = outcomeIndex,
|
||||||
|
paramsOpt = paramsOpt
|
||||||
|
)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
offerOutcome <-
|
offerOutcome <-
|
||||||
|
|
Loading…
Add table
Reference in a new issue