2022 09 29 handle unordered sigs (#4807)

* Get unit tests passing for unordered nonces/signatures

* Get v0 oracle announcements working again on listannouncements

* Add unit test to make sure we can still validate signatures for attestments with nonces out of order
This commit is contained in:
Chris Stewart 2022-09-29 11:43:50 -05:00 committed by GitHub
parent 2c85f92b18
commit 34e023e93f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 226 additions and 108 deletions

View File

@ -936,7 +936,7 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
val dummyOracleAttestment = val dummyOracleAttestment =
OracleAttestmentV0TLV("eventId", OracleAttestmentV0TLV("eventId",
dummyPubKey.schnorrPublicKey, dummyPubKey.schnorrPublicKey,
OrderedSchnorrSignatures(dummyOracleSig), OrderedSchnorrSignatures(dummyOracleSig).toVector,
Vector("outcome")) Vector("outcome"))
lazy val winStr: String = "WIN" lazy val winStr: String = "WIN"

View File

@ -36,11 +36,15 @@ sealed trait OracleEvent {
def eventDescriptorTLV: EventDescriptorTLV def eventDescriptorTLV: EventDescriptorTLV
def eventTLV: OracleEventTLV = def eventTLV: OracleEventTLV = {
OracleEventV0TLV(nonces, require(eventDbsOpt.isDefined,
s"Event dbs must be defined to figure out ordering of nonces")
val v0NonceOrder = eventDbsOpt.get.sortBy(_.nonceIndex).map(_.nonce)
OracleEventV0TLV(v0NonceOrder,
UInt32(maturationTime.getEpochSecond), UInt32(maturationTime.getEpochSecond),
eventDescriptorTLV, eventDescriptorTLV,
eventName) eventName)
}
def announcementTLV: OracleAnnouncementTLV = { def announcementTLV: OracleAnnouncementTLV = {
eventTLV match { eventTLV match {
@ -48,6 +52,11 @@ sealed trait OracleEvent {
OracleAnnouncementV0TLV(announcementSignature, pubkey, v0TLV) OracleAnnouncementV0TLV(announcementSignature, pubkey, v0TLV)
} }
} }
/** These are needed for old announcements/attesatations that do not follow the requirement
* to order nonces
*/
protected def eventDbsOpt: Option[Vector[EventDb]]
} }
/** An oracle event that has not been signed yet */ /** An oracle event that has not been signed yet */
@ -67,11 +76,22 @@ sealed trait CompletedOracleEvent extends OracleEvent {
OrderedSchnorrSignatures.fromUnsorted(unsorted) OrderedSchnorrSignatures.fromUnsorted(unsorted)
} }
def oracleAttestmentV0TLV: OracleAttestmentV0TLV = def oracleAttestmentV0TLV: OracleAttestmentV0TLV = {
OracleAttestmentV0TLV(eventName,
pubkey, announcementTLV match {
signatures, case ann: OracleAnnouncementV0TLV =>
outcomes.map(_.outcomeString)) //v0 announcements do not have a invariant stating that nonces neeed to be sorted
//a specific way, so we need to use the unsorted variant to make sure
//announcementSignatures evaluate to true
val unsorted = ann.eventTLV.nonces
.zip(attestations)
.map(sigPieces => SchnorrDigitalSignature(sigPieces._1, sigPieces._2))
OracleAttestmentV0TLV(eventName,
pubkey,
unsorted,
outcomes.map(_.outcomeString))
}
}
def outcomes: Vector[DLCAttestationType] def outcomes: Vector[DLCAttestationType]
@ -92,7 +112,8 @@ case class PendingEnumV0OracleEvent(
signingVersion: SigningVersion, signingVersion: SigningVersion,
maturationTime: Instant, maturationTime: Instant,
announcementSignature: SchnorrDigitalSignature, announcementSignature: SchnorrDigitalSignature,
eventDescriptorTLV: EnumEventDescriptorV0TLV) eventDescriptorTLV: EnumEventDescriptorV0TLV,
eventDbsOpt: Option[Vector[EventDb]])
extends PendingOracleEvent extends PendingOracleEvent
with EnumV0OracleEvent with EnumV0OracleEvent
@ -105,13 +126,16 @@ case class CompletedEnumV0OracleEvent(
announcementSignature: SchnorrDigitalSignature, announcementSignature: SchnorrDigitalSignature,
eventDescriptorTLV: EnumEventDescriptorV0TLV, eventDescriptorTLV: EnumEventDescriptorV0TLV,
outcome: EnumAttestation, outcome: EnumAttestation,
attestation: FieldElement) attestation: FieldElement,
eventDbsOpt: Option[Vector[EventDb]])
extends CompletedOracleEvent extends CompletedOracleEvent
with EnumV0OracleEvent { with EnumV0OracleEvent {
require(OracleEvent.verifyAttestations(announcementTLV, require(
oracleAttestmentV0TLV, OracleEvent.verifyAttestations(announcementTLV,
signingVersion), oracleAttestmentV0TLV,
"Signatures given are invalid") signingVersion),
s"Signatures given are invalid, eventId=${announcementTLV.eventTLV.eventId}"
)
override def attestations: Vector[FieldElement] = Vector(attestation) override def attestations: Vector[FieldElement] = Vector(attestation)
@ -131,7 +155,8 @@ case class PendingDigitDecompositionV0OracleEvent(
signingVersion: SigningVersion, signingVersion: SigningVersion,
maturationTime: Instant, maturationTime: Instant,
announcementSignature: SchnorrDigitalSignature, announcementSignature: SchnorrDigitalSignature,
eventDescriptorTLV: DigitDecompositionEventDescriptorV0TLV) eventDescriptorTLV: DigitDecompositionEventDescriptorV0TLV,
eventDbsOpt: Option[Vector[EventDb]])
extends PendingOracleEvent extends PendingOracleEvent
with DigitDecompositionV0OracleEvent with DigitDecompositionV0OracleEvent
@ -144,14 +169,17 @@ case class CompletedDigitDecompositionV0OracleEvent(
announcementSignature: SchnorrDigitalSignature, announcementSignature: SchnorrDigitalSignature,
eventDescriptorTLV: DigitDecompositionEventDescriptorV0TLV, eventDescriptorTLV: DigitDecompositionEventDescriptorV0TLV,
dlcOutcome: NumericDLCOutcomeType, dlcOutcome: NumericDLCOutcomeType,
attestations: Vector[FieldElement]) attestations: Vector[FieldElement],
eventDbsOpt: Option[Vector[EventDb]])
extends CompletedOracleEvent extends CompletedOracleEvent
with DigitDecompositionV0OracleEvent { with DigitDecompositionV0OracleEvent {
require(OracleEvent.verifyAttestations(announcementTLV, require(
oracleAttestmentV0TLV, OracleEvent.verifyAttestations(announcementTLV,
signingVersion), oracleAttestmentV0TLV,
"Signatures given are invalid") signingVersion),
s"Signatures given are invalid for eventId=${announcementTLV.eventTLV.eventId}"
)
val outcomeBase10: Long = { val outcomeBase10: Long = {
val (digits, positive) = dlcOutcome match { val (digits, positive) = dlcOutcome match {
@ -199,7 +227,8 @@ object OracleEvent {
eventDb.announcementSignature, eventDb.announcementSignature,
enum, enum,
EnumAttestation(eventDb.outcomeOpt.get), EnumAttestation(eventDb.outcomeOpt.get),
sig sig,
Some(eventDbs)
) )
case (enum: EnumEventDescriptorV0TLV, None) => case (enum: EnumEventDescriptorV0TLV, None) =>
require(eventDbs.size == 1, "Enum events may only have one eventDb") require(eventDbs.size == 1, "Enum events may only have one eventDb")
@ -209,7 +238,8 @@ object OracleEvent {
eventDb.signingVersion, eventDb.signingVersion,
eventDb.maturationTime, eventDb.maturationTime,
eventDb.announcementSignature, eventDb.announcementSignature,
enum) enum,
Some(eventDbs))
case (decomp: DigitDecompositionEventDescriptorV0TLV, Some(_)) => case (decomp: DigitDecompositionEventDescriptorV0TLV, Some(_)) =>
require(eventDbs.forall(_.attestationOpt.isDefined), require(eventDbs.forall(_.attestationOpt.isDefined),
"Cannot have a partially signed event") "Cannot have a partially signed event")
@ -230,7 +260,6 @@ object OracleEvent {
} }
UnsignedNumericOutcome(digits) UnsignedNumericOutcome(digits)
} }
CompletedDigitDecompositionV0OracleEvent( CompletedDigitDecompositionV0OracleEvent(
eventDb.pubkey, eventDb.pubkey,
OrderedNonces.fromUnsorted(sortedEventDbs.map(_.nonce)), OrderedNonces.fromUnsorted(sortedEventDbs.map(_.nonce)),
@ -240,7 +269,8 @@ object OracleEvent {
eventDb.announcementSignature, eventDb.announcementSignature,
decomp, decomp,
dlcOutcome, dlcOutcome,
attestations attestations,
Some(eventDbs)
) )
case (decomp: DigitDecompositionEventDescriptorV0TLV, None) => case (decomp: DigitDecompositionEventDescriptorV0TLV, None) =>
require(eventDbs.forall(_.attestationOpt.isEmpty), require(eventDbs.forall(_.attestationOpt.isEmpty),
@ -255,7 +285,8 @@ object OracleEvent {
eventDb.signingVersion, eventDb.signingVersion,
eventDb.maturationTime, eventDb.maturationTime,
eventDb.announcementSignature, eventDb.announcementSignature,
decomp decomp,
Some(eventDbs)
) )
} }
} }
@ -267,8 +298,14 @@ object OracleEvent {
attestationTLV: OracleAttestmentTLV, attestationTLV: OracleAttestmentTLV,
signingVersion: SigningVersion): Boolean = { signingVersion: SigningVersion): Boolean = {
val tlvOutcomes = attestationTLV.outcomes val tlvOutcomes = attestationTLV.outcomes
val attestations = attestationTLV.sigs val attestations = attestationTLV match {
val nonces = announcement.eventTLV.nonces case v0: OracleAttestmentV0TLV =>
v0.unsortedSignatures
}
val nonces = announcement.eventTLV match {
case v0: OracleEventV0TLV =>
v0.nonces
}
if ( if (
announcement.publicKey != attestationTLV.publicKey || announcement.publicKey != attestationTLV.publicKey ||
nonces.size != attestations.size || nonces.size != attestations.size ||

View File

@ -7,6 +7,7 @@ import org.bitcoins.core.protocol.dlc.models.{
} }
import org.bitcoins.core.protocol.tlv.{ import org.bitcoins.core.protocol.tlv.{
EnumOutcome, EnumOutcome,
OracleEventV0TLV,
SignedNumericOutcome, SignedNumericOutcome,
UnsignedNumericOutcome UnsignedNumericOutcome
} }
@ -137,7 +138,10 @@ object DLCAdaptorPointComputer {
contractInfo.oracleInfo.singleOracleInfos.map { info => contractInfo.oracleInfo.singleOracleInfos.map { info =>
val announcement = info.announcement val announcement = info.announcement
val pubKey = announcement.publicKey val pubKey = announcement.publicKey
val nonces = announcement.eventTLV.nonces.toVector.map(_.publicKey) val nonces = announcement.eventTLV match {
case v0: OracleEventV0TLV =>
v0.nonces.map(_.publicKey)
}
nonces.map { nonce => nonces.map { nonce =>
possibleOutcomes.map { outcome => possibleOutcomes.map { outcome =>

View File

@ -16,7 +16,8 @@ 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.{ import org.bitcoins.core.protocol.tlv.{
OracleAnnouncementTLV, OracleAnnouncementTLV,
OracleAttestmentTLV OracleAttestmentTLV,
OracleEventV0TLV
} }
import org.bitcoins.core.protocol.transaction.{Transaction, WitnessTransaction} import org.bitcoins.core.protocol.transaction.{Transaction, WitnessTransaction}
import org.bitcoins.core.util.sorted.{OrderedAnnouncements, OrderedNonces} import org.bitcoins.core.util.sorted.{OrderedAnnouncements, OrderedNonces}
@ -262,7 +263,12 @@ object DLCUtil {
oracleSignatures: Vector[OracleSignatures]): Option[OracleSignatures] = { oracleSignatures: Vector[OracleSignatures]): Option[OracleSignatures] = {
val announcementNonces: Vector[Vector[SchnorrNonce]] = { val announcementNonces: Vector[Vector[SchnorrNonce]] = {
announcements announcements
.map(_.eventTLV.nonces) .map { ann =>
ann.eventTLV match {
case v0: OracleEventV0TLV =>
v0.nonces
}
}
.map(_.toVector) .map(_.toVector)
} }
val resultOpt = oracleSignatures.find { case oracleSignature => val resultOpt = oracleSignatures.find { case oracleSignature =>
@ -324,9 +330,13 @@ object DLCUtil {
// Nonces should be unique so searching for the first nonce should be safe // Nonces should be unique so searching for the first nonce should be safe
val firstNonce = sig.sigs.head.rx val firstNonce = sig.sigs.head.rx
announcements announcements
.find( .find { ann =>
_.eventTLV.nonces.headOption ann.eventTLV match {
.contains(firstNonce)) match { case v0: OracleEventV0TLV =>
v0.nonces.headOption
.contains(firstNonce)
}
} match {
case Some(announcement) => case Some(announcement) =>
acc :+ OracleSignatures(SingleOracleInfo(announcement), sig.sigs) acc :+ OracleSignatures(SingleOracleInfo(announcement), sig.sigs)
case None => case None =>

View File

@ -138,8 +138,7 @@ object DLCExecutor {
): ExecutedDLCOutcome = { ): ExecutedDLCOutcome = {
require( require(
DLCUtil.checkOracleSignaturesAgainstContract(contractInfo, oracleSigs), DLCUtil.checkOracleSignaturesAgainstContract(contractInfo, oracleSigs),
s"Incorrect oracle signatures and contract combination, got=${oracleSigs} contractInfo.announcement=${contractInfo.oracleInfos s"Incorrect oracle signatures and contract combination, got=${oracleSigs}"
.map(_.singleOracleInfos.map(_.announcement.eventTLV.nonces))}"
) )
val sigOracles = oracleSigs.map(_.oracle) val sigOracles = oracleSigs.map(_.oracle)

View File

@ -69,7 +69,12 @@ sealed trait SingleOracleInfo
def publicKey: SchnorrPublicKey = announcement.publicKey def publicKey: SchnorrPublicKey = announcement.publicKey
/** The oracle's pre-committed nonces, in the correct order */ /** The oracle's pre-committed nonces, in the correct order */
def nonces: OrderedNonces = announcement.eventTLV.nonces def nonces: OrderedNonces = {
announcement.eventTLV match {
case v0: OracleEventV0TLV =>
OrderedNonces(v0.nonces)
}
}
/** The order of the given sigs should correspond to the given outcome. */ /** The order of the given sigs should correspond to the given outcome. */
def verifySigs(outcome: DLCOutcomeType, sigs: OracleSignatures): Boolean def verifySigs(outcome: DLCOutcomeType, sigs: OracleSignatures): Boolean
@ -125,7 +130,12 @@ case class EnumSingleOracleInfo(announcement: OracleAnnouncementTLV)
.isInstanceOf[EnumEventDescriptorV0TLV], .isInstanceOf[EnumEventDescriptorV0TLV],
s"Enum OracleInfo requires EnumEventDescriptor, $announcement") s"Enum OracleInfo requires EnumEventDescriptor, $announcement")
val nonce: SchnorrNonce = announcement.eventTLV.nonces.head val nonce: SchnorrNonce = {
announcement.eventTLV match {
case v0: OracleEventV0TLV =>
v0.nonces.head
}
}
/** @inheritdoc */ /** @inheritdoc */
override def verifySigs( override def verifySigs(

View File

@ -766,13 +766,12 @@ object DigitDecompositionEventDescriptorV0TLV
sealed trait OracleEventTLV extends DLCOracleTLV { sealed trait OracleEventTLV extends DLCOracleTLV {
def eventDescriptor: EventDescriptorTLV def eventDescriptor: EventDescriptorTLV
def nonces: OrderedNonces
def eventId: NormalizedString def eventId: NormalizedString
def eventMaturityEpoch: UInt32 def eventMaturityEpoch: UInt32
} }
case class OracleEventV0TLV( case class OracleEventV0TLV(
nonces: OrderedNonces, nonces: Vector[SchnorrNonce],
eventMaturityEpoch: UInt32, eventMaturityEpoch: UInt32,
eventDescriptor: EventDescriptorTLV, eventDescriptor: EventDescriptorTLV,
eventId: NormalizedString eventId: NormalizedString
@ -786,7 +785,7 @@ case class OracleEventV0TLV(
override def tpe: BigSizeUInt = OracleEventV0TLV.tpe override def tpe: BigSizeUInt = OracleEventV0TLV.tpe
override val value: ByteVector = { override val value: ByteVector = {
u16PrefixedList(nonces.toVector) ++ u16PrefixedList(nonces) ++
eventMaturityEpoch.bytes ++ eventMaturityEpoch.bytes ++
eventDescriptor.bytes ++ eventDescriptor.bytes ++
strBytes(eventId) strBytes(eventId)
@ -809,10 +808,7 @@ object OracleEventV0TLV extends TLVFactory[OracleEventV0TLV] {
val eventDescriptor = iter.take(EventDescriptorTLV) val eventDescriptor = iter.take(EventDescriptorTLV)
val eventId = iter.takeString() val eventId = iter.takeString()
OracleEventV0TLV(OrderedNonces.fromUnsorted(nonces), OracleEventV0TLV(nonces, eventMaturity, eventDescriptor, eventId)
eventMaturity,
eventDescriptor,
eventId)
} }
override val typeName: String = "OracleEventV0TLV" override val typeName: String = "OracleEventV0TLV"
@ -868,11 +864,10 @@ object OracleAnnouncementV0TLV extends TLVFactory[OracleAnnouncementV0TLV] {
lazy val dummy: OracleAnnouncementV0TLV = { lazy val dummy: OracleAnnouncementV0TLV = {
val dummyPrivKey: ECPrivateKey = ECPrivateKey.fromHex( val dummyPrivKey: ECPrivateKey = ECPrivateKey.fromHex(
"f04671ab68f3fefbeaa344c49149748f722287a81b19cd956b2332d07b8f6853") "f04671ab68f3fefbeaa344c49149748f722287a81b19cd956b2332d07b8f6853")
val event = OracleEventV0TLV( val event = OracleEventV0TLV(Vector(dummyPrivKey.schnorrNonce),
OrderedNonces.fromUnsorted(Vector(dummyPrivKey.schnorrNonce)), UInt32.zero,
UInt32.zero, EnumEventDescriptorV0TLV.dummy,
EnumEventDescriptorV0TLV.dummy, "dummy")
"dummy")
val sig = val sig =
dummyPrivKey.schnorrSign( dummyPrivKey.schnorrSign(
CryptoUtil.sha256DLCAnnouncement(event.bytes).bytes) CryptoUtil.sha256DLCAnnouncement(event.bytes).bytes)
@ -885,7 +880,7 @@ object OracleAnnouncementV0TLV extends TLVFactory[OracleAnnouncementV0TLV] {
nonce: SchnorrNonce, nonce: SchnorrNonce,
events: Vector[EnumOutcome]): OracleAnnouncementTLV = { events: Vector[EnumOutcome]): OracleAnnouncementTLV = {
val event = OracleEventV0TLV( val event = OracleEventV0TLV(
OrderedNonces.fromUnsorted(Vector(nonce)), Vector(nonce),
UInt32.zero, UInt32.zero,
EnumEventDescriptorV0TLV(events.map(outcome => outcome.outcome)), EnumEventDescriptorV0TLV(events.map(outcome => outcome.outcome)),
"dummy") "dummy")
@ -904,7 +899,8 @@ object OracleAnnouncementV0TLV extends TLVFactory[OracleAnnouncementV0TLV] {
nonces.length, nonces.length,
"dummy", "dummy",
Int32.zero) Int32.zero)
val event = OracleEventV0TLV(nonces, UInt32.zero, eventDescriptor, "dummy") val event =
OracleEventV0TLV(nonces.toVector, UInt32.zero, eventDescriptor, "dummy")
val sig = val sig =
privKey.schnorrSign(CryptoUtil.sha256DLCAnnouncement(event.bytes).bytes) privKey.schnorrSign(CryptoUtil.sha256DLCAnnouncement(event.bytes).bytes)
@ -933,15 +929,22 @@ object OracleAttestmentTLV extends TLVParentFactory[OracleAttestmentTLV] {
case class OracleAttestmentV0TLV( case class OracleAttestmentV0TLV(
eventId: NormalizedString, eventId: NormalizedString,
publicKey: SchnorrPublicKey, publicKey: SchnorrPublicKey,
sigs: OrderedSchnorrSignatures, unsortedSignatures: Vector[SchnorrDigitalSignature],
outcomes: Vector[NormalizedString]) outcomes: Vector[NormalizedString])
extends OracleAttestmentTLV { extends OracleAttestmentTLV {
require(sigs.nonEmpty, "Cannot have 0 signatures") require(unsortedSignatures.nonEmpty, "Cannot have 0 signatures")
require( require(
outcomes.size == sigs.size, outcomes.size == unsortedSignatures.size,
s"Number of outcomes must match number of signatures, ${outcomes.size} != ${sigs.size}") s"Number of outcomes must match number of signatures, ${outcomes.size} != ${sigs.size}")
override val tpe: BigSizeUInt = OracleAttestmentV0TLV.tpe override val tpe: BigSizeUInt = OracleAttestmentV0TLV.tpe
/** This should be used very carefully with v0 attestments. We do not have a requirement in the
* in the original protocol that signatures are sorted. If you are seeing signature verification
* failing you probably need to be using [[unsortedSignatures]] rather than [[sigs]]
*/
override val sigs: OrderedSchnorrSignatures =
OrderedSchnorrSignatures.fromUnsorted(unsortedSignatures)
override val value: ByteVector = { override val value: ByteVector = {
val outcomesBytes = outcomes.foldLeft(ByteVector.empty) { val outcomesBytes = outcomes.foldLeft(ByteVector.empty) {
case (accum, elem) => case (accum, elem) =>
@ -963,16 +966,13 @@ object OracleAttestmentV0TLV extends TLVFactory[OracleAttestmentV0TLV] {
val eventId = iter.takeString() val eventId = iter.takeString()
val pubKey = iter.take(SchnorrPublicKey, 32) val pubKey = iter.take(SchnorrPublicKey, 32)
val sigs = val unsortedSigs =
iter.takeU16PrefixedList(() => iter.take(SchnorrDigitalSignature, 64)) iter.takeU16PrefixedList(() => iter.take(SchnorrDigitalSignature, 64))
val outcomes = sigs.indices.toVector.map { _ => val outcomes = unsortedSigs.indices.toVector.map { _ =>
iter.takeString() iter.takeString()
} }
OracleAttestmentV0TLV(eventId, OracleAttestmentV0TLV(eventId, pubKey, unsortedSigs, outcomes)
pubKey,
OrderedSchnorrSignatures.fromUnsorted(sigs),
outcomes)
} }
lazy val dummy: OracleAttestmentV0TLV = { lazy val dummy: OracleAttestmentV0TLV = {
@ -983,7 +983,7 @@ object OracleAttestmentV0TLV extends TLVFactory[OracleAttestmentV0TLV] {
OracleAttestmentV0TLV(eventId, OracleAttestmentV0TLV(eventId,
key.schnorrPublicKey, key.schnorrPublicKey,
OrderedSchnorrSignatures(sig), Vector(sig),
Vector(outcome)) Vector(outcome))
} }

View File

@ -25,8 +25,7 @@ class AttestationVerificationTest extends BitcoinSUnitTest {
val invalidEnumAttestation: OracleAttestmentV0TLV = { val invalidEnumAttestation: OracleAttestmentV0TLV = {
val unsorted = validEnumAttestation.sigs.map(_.copy(sig = FieldElement.one)) val unsorted = validEnumAttestation.sigs.map(_.copy(sig = FieldElement.one))
validEnumAttestation.copy(sigs = validEnumAttestation.copy(unsortedSignatures = unsorted.toVector)
OrderedSchnorrSignatures.fromUnsorted(unsorted.toVector))
} }
val unsignedDigitDecompAnnouncement: OracleAnnouncementV0TLV = val unsignedDigitDecompAnnouncement: OracleAnnouncementV0TLV =
@ -42,7 +41,8 @@ class AttestationVerificationTest extends BitcoinSUnitTest {
val unsorted = validUnsignedDigitDecompAttestation.sigs.map( val unsorted = validUnsignedDigitDecompAttestation.sigs.map(
_.copy(sig = FieldElement.one)) _.copy(sig = FieldElement.one))
val sorted = OrderedSchnorrSignatures.fromUnsorted(unsorted.toVector) val sorted = OrderedSchnorrSignatures.fromUnsorted(unsorted.toVector)
validUnsignedDigitDecompAttestation.copy(sigs = sorted) validUnsignedDigitDecompAttestation.copy(unsortedSignatures =
sorted.toVector)
} }
// this one was generated with a different public key // this one was generated with a different public key
@ -62,7 +62,7 @@ class AttestationVerificationTest extends BitcoinSUnitTest {
val unsorted = val unsorted =
validSignedDigitDecompAttestation.sigs.map(_.copy(sig = FieldElement.one)) validSignedDigitDecompAttestation.sigs.map(_.copy(sig = FieldElement.one))
val sorted = OrderedSchnorrSignatures.fromUnsorted(unsorted.toVector) val sorted = OrderedSchnorrSignatures.fromUnsorted(unsorted.toVector)
validSignedDigitDecompAttestation.copy(sigs = sorted) validSignedDigitDecompAttestation.copy(unsortedSignatures = sorted.toVector)
} }
val invalidSignedDigitDecompAttestation1: OracleAttestmentV0TLV = val invalidSignedDigitDecompAttestation1: OracleAttestmentV0TLV =
@ -132,4 +132,35 @@ class AttestationVerificationTest extends BitcoinSUnitTest {
invalidSignedDigitDecompAttestation1, invalidSignedDigitDecompAttestation1,
signingVersion)) signingVersion))
} }
it must "validate announcement/attesation with out of order nonces" in {
val annHex =
"fdd824fd01661aabd9bbcae0207aff9510f05099295f32ff72290cf5c494d3869f582f8c4a6cf1b7d832562047f68ce607eb39" +
"dcd7ec8ce64432dc51a8853dc5a3acd96a8bc5545aa0024da81c3fec63e56e07ee141cbefbd2c6e7d4dede124fe856ea453a85fdd822fd010" +
"000070652285e89487dc8ce816a81234082394d9602263d35a7322b77299082257767b559c622def4bba15a2ad7fc336edd71ace9b4c366b9" +
"eaba22b73df00589e74b7ca5b6c7301ef7dc62aeae1823018107868d8956677421e11ffd8f125f2fedf4a527003355640ee9333cda6c37a92" +
"d4989c6ab96eddc9266f0ddce0e2a3ffb77aa9eefa1fe40eddd0fa63f501e9b368eed6ab0cc0d2e5e6da1baa570ed9e857134bbc8a15dd594" +
"9eb1203b1d15ae701fe4b04707a1ea54c10fef16308bf806f2aa0b17f8673fe785f6b9ff0718e55b621c8e9d92839759a98b88bd6590a0ff8" +
"56011fe80fdd80a0f00020105756e697473000000000006067369676e6564"
val annV0 = OracleAnnouncementV0TLV.fromHex(annHex)
val attestationHex =
"fdd868fd01f7067369676e6564545aa0024da81c3fec63e56e07ee141cbefbd2c6e7d4dede124fe856ea453a8500" +
"070652285e89487dc8ce816a81234082394d9602263d35a7322b772990822577670ab288b31d99f56d18d4f34be875c0a4d73aae135c4f503" +
"49a1014b686d69841b559c622def4bba15a2ad7fc336edd71ace9b4c366b9eaba22b73df00589e74ba7b82eef2041bf6af1511016cadabe9d" +
"52e64d875caf5bfef85903dbc4fc00737ca5b6c7301ef7dc62aeae1823018107868d8956677421e11ffd8f125f2fedf469f40edbb7846274f" +
"46a973f5442ece91b5a6e450a8cdcef272058a27176dabba527003355640ee9333cda6c37a92d4989c6ab96eddc9266f0ddce0e2a3ffb7762" +
"f10e09f79273ab04d1549c1d60054738fe903575aa732760bd2668530459e9aa9eefa1fe40eddd0fa63f501e9b368eed6ab0cc0d2e5e6da1b" +
"aa570ed9e857188bae5c59c9ac89bec3fa00c8e1b725789e1af15f7b256ae7f169edfe7f3ef8834bbc8a15dd5949eb1203b1d15ae701fe4b0" +
"4707a1ea54c10fef16308bf806f2144d3e7aa63705924da607162f59bff757490469c2c8d4e62a1aa0bf27323bcdaa0b17f8673fe785f6b9f" +
"f0718e55b621c8e9d92839759a98b88bd6590a0ff85e072b65d258bbfdc653444b08714c2be395b7e645caa214567b22916ffb7ebeb012d01" +
"3001310130013101300130"
val attestationV0 = OracleAttestmentV0TLV.fromHex(attestationHex)
val result = OracleEvent.verifyAttestations(annV0,
attestationV0,
SigningVersion.latest)
assert(result)
}
} }

View File

@ -11,7 +11,6 @@ import org.bitcoins.core.protocol.dlc.compute.SigningVersion
import org.bitcoins.core.protocol.script.P2WPKHWitnessSPKV0 import org.bitcoins.core.protocol.script.P2WPKHWitnessSPKV0
import org.bitcoins.core.protocol.tlv._ import org.bitcoins.core.protocol.tlv._
import org.bitcoins.core.util.TimeUtil import org.bitcoins.core.util.TimeUtil
import org.bitcoins.core.util.sorted.OrderedNonces
import org.bitcoins.crypto._ import org.bitcoins.crypto._
import org.bitcoins.testkit.fixtures.DLCOracleFixture import org.bitcoins.testkit.fixtures.DLCOracleFixture
import org.bitcoins.testkitcore.Implicits._ import org.bitcoins.testkitcore.Implicits._
@ -242,7 +241,7 @@ class DLCOracleTest extends DLCOracleFixture {
assert(event.maturationTime.getEpochSecond == time.getEpochSecond) assert(event.maturationTime.getEpochSecond == time.getEpochSecond)
val expectedEventTLV = val expectedEventTLV =
OracleEventV0TLV(OrderedNonces(event.nonces.head), OracleEventV0TLV(event.nonces.toVector,
UInt32(event.maturationTime.getEpochSecond), UInt32(event.maturationTime.getEpochSecond),
testDescriptor, testDescriptor,
eventName) eventName)
@ -560,7 +559,11 @@ class DLCOracleTest extends DLCOracleFixture {
_ = assert(beforeEvents.size == 1) _ = assert(beforeEvents.size == 1)
_ = assert(beforeEvents.head.isInstanceOf[PendingOracleEvent]) _ = assert(beforeEvents.head.isInstanceOf[PendingOracleEvent])
nonce = announcement.eventTLV.nonces.head nonce = {
announcement.eventTLV match {
case v0: OracleEventV0TLV => v0.nonces.head
}
}
_ <- dlcOracle.createAttestation(nonce, EnumAttestation(outcome)) _ <- dlcOracle.createAttestation(nonce, EnumAttestation(outcome))
afterPending <- dlcOracle.listPendingEventDbs() afterPending <- dlcOracle.listPendingEventDbs()
@ -589,7 +592,9 @@ class DLCOracleTest extends DLCOracleFixture {
futureTime, futureTime,
enumOutcomes) enumOutcomes)
nonce = announcement.eventTLV.nonces.head nonce = announcement.eventTLV match {
case v0: OracleEventV0TLV => v0.nonces.head
}
_ <- dlcOracle.createAttestation( _ <- dlcOracle.createAttestation(
nonce, nonce,

View File

@ -248,7 +248,7 @@ case class DLCOracle()(implicit val conf: DLCOracleAppConfig)
nonces = rValueDbs.map(_.nonce) nonces = rValueDbs.map(_.nonce)
eventTLV = OracleEventV0TLV(OrderedNonces.fromUnsorted(nonces), eventTLV = OracleEventV0TLV(OrderedNonces.fromUnsorted(nonces).toVector,
epoch, epoch,
descriptor, descriptor,
eventName) eventName)
@ -400,8 +400,10 @@ case class DLCOracle()(implicit val conf: DLCOracleAppConfig)
eventDescriptorTLV match { eventDescriptorTLV match {
case _: SignedDigitDecompositionEventDescriptor => case _: SignedDigitDecompositionEventDescriptor =>
val signOutcome = DigitDecompositionSignAttestation(num >= 0) val signOutcome = DigitDecompositionSignAttestation(num >= 0)
createAttestation(oracleEventTLV.nonces.head, signOutcome).map(db => val signNonce = oracleEventTLV match {
Vector(db)) case v0: OracleEventV0TLV => v0.nonces.head
}
createAttestation(signNonce, signOutcome).map(db => Vector(db))
case _: UnsignedDigitDecompositionEventDescriptor => case _: UnsignedDigitDecompositionEventDescriptor =>
FutureUtil.emptyVec[EventDb] FutureUtil.emptyVec[EventDb]
} }
@ -420,11 +422,15 @@ case class DLCOracle()(implicit val conf: DLCOracleAppConfig)
eventDescriptorTLV.base.toInt, eventDescriptorTLV.base.toInt,
eventDescriptorTLV.numDigits.toInt) eventDescriptorTLV.numDigits.toInt)
val oracleEventNonces = oracleEventTLV match {
case v0: OracleEventV0TLV =>
v0.nonces
}
val nonces = eventDescriptorTLV match { val nonces = eventDescriptorTLV match {
case _: UnsignedDigitDecompositionEventDescriptor => case _: UnsignedDigitDecompositionEventDescriptor =>
oracleEventTLV.nonces oracleEventNonces
case _: SignedDigitDecompositionEventDescriptor => case _: SignedDigitDecompositionEventDescriptor =>
oracleEventTLV.nonces.tail oracleEventNonces.tail
} }
val digitSigAVecF: Future[ val digitSigAVecF: Future[

View File

@ -2,9 +2,9 @@ package org.bitcoins.dlc.oracle.util
import org.bitcoins.core.api.dlcoracle._ import org.bitcoins.core.api.dlcoracle._
import org.bitcoins.core.api.dlcoracle.db._ import org.bitcoins.core.api.dlcoracle.db._
import org.bitcoins.core.protocol.dlc.compute.SigningVersion
import org.bitcoins.core.protocol.tlv._ import org.bitcoins.core.protocol.tlv._
import org.bitcoins.core.util.sorted.OrderedNonces import org.bitcoins.crypto.SchnorrNonce
import org.bitcoins.core.protocol.dlc.compute.SigningVersion
trait EventDbUtil { trait EventDbUtil {
@ -13,7 +13,9 @@ trait EventDbUtil {
*/ */
def toEventOutcomeDbs( def toEventOutcomeDbs(
descriptor: EventDescriptorTLV, descriptor: EventDescriptorTLV,
nonces: OrderedNonces, nonces: Vector[
SchnorrNonce
], //ugh, can we enforce some sort of invariant here? can i make this method private?
signingVersion: SigningVersion): Vector[EventOutcomeDb] = { signingVersion: SigningVersion): Vector[EventOutcomeDb] = {
descriptor match { descriptor match {
case enum: EnumEventDescriptorV0TLV => case enum: EnumEventDescriptorV0TLV =>
@ -59,9 +61,12 @@ trait EventDbUtil {
oracleAnnouncementV0TLV: OracleAnnouncementV0TLV, oracleAnnouncementV0TLV: OracleAnnouncementV0TLV,
signingVersion: SigningVersion = SigningVersion.latest): Vector[ signingVersion: SigningVersion = SigningVersion.latest): Vector[
EventOutcomeDb] = { EventOutcomeDb] = {
val oracleEventV0 = oracleAnnouncementV0TLV.eventTLV match {
case v0: OracleEventV0TLV => v0
}
toEventOutcomeDbs(descriptor = toEventOutcomeDbs(descriptor =
oracleAnnouncementV0TLV.eventTLV.eventDescriptor, oracleAnnouncementV0TLV.eventTLV.eventDescriptor,
nonces = oracleAnnouncementV0TLV.eventTLV.nonces, nonces = oracleEventV0.nonces,
signingVersion = signingVersion) signingVersion = signingVersion)
} }

View File

@ -16,7 +16,6 @@ import org.bitcoins.core.protocol.dlc.models.{
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.script.util.PreviousOutputMap import org.bitcoins.core.script.util.PreviousOutputMap
import org.bitcoins.core.util.sorted.OrderedSchnorrSignatures
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
import org.bitcoins.testkit.wallet.DLCWalletUtil._ import org.bitcoins.testkit.wallet.DLCWalletUtil._
import org.bitcoins.testkit.wallet.{BitcoinSDualWalletTest, DLCWalletUtil} import org.bitcoins.testkit.wallet.{BitcoinSDualWalletTest, DLCWalletUtil}
@ -458,11 +457,12 @@ class DLCExecutionTest extends BitcoinSDualWalletTest {
//of invariants in OracleAttestmentV0TLV //of invariants in OracleAttestmentV0TLV
badSigs = goodAttestment.sigs.dropRight(1) badSigs = goodAttestment.sigs.dropRight(1)
badOutcomes = goodAttestment.outcomes.dropRight(1) badOutcomes = goodAttestment.outcomes.dropRight(1)
badAttestment = OracleAttestmentV0TLV( badAttestment = OracleAttestmentV0TLV(eventId = goodAttestment.eventId,
eventId = goodAttestment.eventId, publicKey =
publicKey = goodAttestment.publicKey, goodAttestment.publicKey,
sigs = OrderedSchnorrSignatures.fromUnsorted(badSigs.toVector), unsortedSignatures =
outcomes = badOutcomes) badSigs.toVector,
outcomes = badOutcomes)
func = (wallet: DLCWallet) => func = (wallet: DLCWallet) =>
wallet.executeDLC(contractId, badAttestment).map(_.get) wallet.executeDLC(contractId, badAttestment).map(_.get)

View File

@ -80,7 +80,7 @@ class DLCMultiOracleEnumExecutionTest extends BitcoinSDualWalletTest {
val initiatorWinSig = priv.schnorrSignWithNonce(hash, kValue) val initiatorWinSig = priv.schnorrSignWithNonce(hash, kValue)
OracleAttestmentV0TLV(eventId, OracleAttestmentV0TLV(eventId,
priv.schnorrPublicKey, priv.schnorrPublicKey,
OrderedSchnorrSignatures(initiatorWinSig), OrderedSchnorrSignatures(initiatorWinSig).toVector,
Vector(initiatorWinStr)) Vector(initiatorWinStr))
} }
@ -101,7 +101,7 @@ class DLCMultiOracleEnumExecutionTest extends BitcoinSDualWalletTest {
val recipientWinSig = priv.schnorrSignWithNonce(hash, kValue) val recipientWinSig = priv.schnorrSignWithNonce(hash, kValue)
OracleAttestmentV0TLV(eventId, OracleAttestmentV0TLV(eventId,
priv.schnorrPublicKey, priv.schnorrPublicKey,
OrderedSchnorrSignatures(recipientWinSig), OrderedSchnorrSignatures(recipientWinSig).toVector,
Vector(recipientWinStr)) Vector(recipientWinStr))
} }

View File

@ -243,10 +243,11 @@ class DLCMultiOracleExactNumericExecutionTest extends BitcoinSDualWalletTest {
require(kValues.length == sigs.length, require(kValues.length == sigs.length,
s"kValues.length=${kValues.length} sigs.length=${sigs.length}") s"kValues.length=${kValues.length} sigs.length=${sigs.length}")
OracleAttestmentV0TLV(eventId, OracleAttestmentV0TLV(
priv.schnorrPublicKey, eventId,
OrderedSchnorrSignatures.fromUnsorted(sigs), priv.schnorrPublicKey,
digitsPadded.map(_.toString)) OrderedSchnorrSignatures.fromUnsorted(sigs).toVector,
digitsPadded.map(_.toString))
} }
} }
} }

View File

@ -253,10 +253,11 @@ class DLCMultiOracleNumericExecutionTest
require(kValues.length == sigs.length, require(kValues.length == sigs.length,
s"kValues.length=${kValues.length} sigs.length=${sigs.length}") s"kValues.length=${kValues.length} sigs.length=${sigs.length}")
OracleAttestmentV0TLV(eventId, OracleAttestmentV0TLV(
priv.schnorrPublicKey, eventId,
OrderedSchnorrSignatures.fromUnsorted(sigs), priv.schnorrPublicKey,
digitsPadded.map(_.toString)) OrderedSchnorrSignatures.fromUnsorted(sigs).toVector,
digitsPadded.map(_.toString))
} }
} }
} }

View File

@ -79,11 +79,11 @@ class DLCNumericExecutionTest extends BitcoinSDualWalletTest {
(OracleAttestmentV0TLV(eventId, (OracleAttestmentV0TLV(eventId,
publicKey, publicKey,
OrderedSchnorrSignatures(initiatorWinSigs), OrderedSchnorrSignatures(initiatorWinSigs).toVector,
initiatorWinVec.map(_.toString)), initiatorWinVec.map(_.toString)),
OracleAttestmentV0TLV(eventId, OracleAttestmentV0TLV(eventId,
publicKey, publicKey,
OrderedSchnorrSignatures(recipientWinSigs), OrderedSchnorrSignatures(recipientWinSigs).toVector,
recipientWinVec.map(_.toString))) recipientWinVec.map(_.toString)))
} }
@ -182,7 +182,8 @@ class DLCNumericExecutionTest extends BitcoinSDualWalletTest {
badAttestment = OracleAttestmentV0TLV(eventId = goodAttestment.eventId, badAttestment = OracleAttestmentV0TLV(eventId = goodAttestment.eventId,
publicKey = publicKey =
goodAttestment.publicKey, goodAttestment.publicKey,
sigs = badSigs, unsortedSignatures =
badSigs.toVector,
outcomes = badOutcomes) outcomes = badOutcomes)
func = (wallet: DLCWallet) => func = (wallet: DLCWallet) =>
wallet.executeDLC(contractId, badAttestment).map(_.get) wallet.executeDLC(contractId, badAttestment).map(_.get)

View File

@ -12,7 +12,7 @@ import org.bitcoins.core.protocol.dlc.sign.DLCTxSigner
import org.bitcoins.core.protocol.dlc.verify.DLCSignatureVerifier import org.bitcoins.core.protocol.dlc.verify.DLCSignatureVerifier
import org.bitcoins.core.protocol.script._ import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.tlv._ import org.bitcoins.core.protocol.tlv._
import org.bitcoins.core.util.sorted.{OrderedAnnouncements, OrderedNonces} import org.bitcoins.core.util.sorted.{OrderedAnnouncements}
import org.bitcoins.core.wallet.utxo._ import org.bitcoins.core.wallet.utxo._
import org.bitcoins.crypto.Sha256Digest import org.bitcoins.crypto.Sha256Digest
import org.bitcoins.db.SafeDatabase import org.bitcoins.db.SafeDatabase
@ -111,7 +111,7 @@ case class DLCDataManagement(dlcWalletDAOs: DLCWalletDAOs)(implicit
if (used) { if (used) {
val nonces = nonceDbs.sortBy(_.index).map(_.nonce) val nonces = nonceDbs.sortBy(_.index).map(_.nonce)
val eventTLV = val eventTLV =
OracleEventV0TLV(OrderedNonces.fromUnsorted(nonces), OracleEventV0TLV(nonces,
data.eventMaturity, data.eventMaturity,
data.eventDescriptor, data.eventDescriptor,
data.eventId) data.eventId)
@ -152,7 +152,7 @@ case class DLCDataManagement(dlcWalletDAOs: DLCWalletDAOs)(implicit
announcementData.find(_.id.contains(id)) match { announcementData.find(_.id.contains(id)) match {
case Some(data) => case Some(data) =>
val nonces = nonceDbs.sortBy(_.index).map(_.nonce) val nonces = nonceDbs.sortBy(_.index).map(_.nonce)
val eventTLV = OracleEventV0TLV(OrderedNonces.fromUnsorted(nonces), val eventTLV = OracleEventV0TLV(nonces,
data.eventMaturity, data.eventMaturity,
data.eventDescriptor, data.eventDescriptor,
data.eventId) data.eventId)

View File

@ -1,6 +1,6 @@
package org.bitcoins.dlc.wallet.models package org.bitcoins.dlc.wallet.models
import org.bitcoins.core.protocol.tlv.OracleAnnouncementTLV import org.bitcoins.core.protocol.tlv.{OracleAnnouncementTLV, OracleEventV0TLV}
import org.bitcoins.crypto._ import org.bitcoins.crypto._
case class OracleNonceDb( case class OracleNonceDb(
@ -17,8 +17,16 @@ object OracleNonceDbHelper {
def fromAnnouncement( def fromAnnouncement(
id: Long, id: Long,
tlv: OracleAnnouncementTLV): Vector[OracleNonceDb] = { tlv: OracleAnnouncementTLV): Vector[OracleNonceDb] = {
tlv.eventTLV.nonces.toVector.zipWithIndex.map { case (nonce, index) => tlv.eventTLV match {
OracleNonceDb(id, index, SchnorrDigitalSignature.dummy, nonce, None, None) case v0: OracleEventV0TLV =>
v0.nonces.zipWithIndex.map { case (nonce, index) =>
OracleNonceDb(id,
index,
SchnorrDigitalSignature.dummy,
nonce,
None,
None)
}
} }
} }

View File

@ -134,7 +134,7 @@ trait TLVGen {
Gen Gen
.listOfN(desc.noncesNeeded, CryptoGenerators.schnorrNonce) .listOfN(desc.noncesNeeded, CryptoGenerators.schnorrNonce)
.map(_.toVector) .map(_.toVector)
} yield OracleEventV0TLV(OrderedNonces.fromUnsorted(nonces), } yield OracleEventV0TLV(OrderedNonces.fromUnsorted(nonces).toVector,
maturity, maturity,
desc, desc,
uri) uri)
@ -162,7 +162,7 @@ trait TLVGen {
Gen Gen
.listOfN(numSigs, StringGenerators.genUTF8String) .listOfN(numSigs, StringGenerators.genUTF8String)
.map(_.toVector) .map(_.toVector)
} yield OracleAttestmentV0TLV(eventId, pubkey, sigs, outcomes) } yield OracleAttestmentV0TLV(eventId, pubkey, sigs.toVector, outcomes)
} }
def contractDescriptorV0TLVWithTotalCollateral: Gen[ def contractDescriptorV0TLVWithTotalCollateral: Gen[

View File

@ -21,7 +21,7 @@ import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
import org.bitcoins.core.script.PreExecutionScriptProgram import org.bitcoins.core.script.PreExecutionScriptProgram
import org.bitcoins.core.script.interpreter.ScriptInterpreter import org.bitcoins.core.script.interpreter.ScriptInterpreter
import org.bitcoins.core.script.util.PreviousOutputMap import org.bitcoins.core.script.util.PreviousOutputMap
import org.bitcoins.core.util.sorted.{OrderedNonces, OrderedSchnorrSignatures} import org.bitcoins.core.util.sorted.OrderedNonces
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
import org.bitcoins.crypto._ import org.bitcoins.crypto._
import org.bitcoins.dlc.wallet.DLCWallet import org.bitcoins.dlc.wallet.DLCWallet
@ -514,11 +514,11 @@ object DLCWalletUtil extends Logging {
(OracleAttestmentV0TLV(eventId, (OracleAttestmentV0TLV(eventId,
publicKey, publicKey,
OrderedSchnorrSignatures(initiatorWinSig), Vector(initiatorWinSig),
Vector(initiatorWinStr)), Vector(initiatorWinStr)),
OracleAttestmentV0TLV(eventId, OracleAttestmentV0TLV(eventId,
publicKey, publicKey,
OrderedSchnorrSignatures(recipientWinSig), Vector(recipientWinSig),
Vector(recipientWinStr))) Vector(recipientWinStr)))
} }