Skip to content

Commit

Permalink
2021 01 15 dlc refactors (#2518)
Browse files Browse the repository at this point in the history
* Kill DigitDecomp.isSigned

* Create TLVEndpoint, TLVMidpoint ADT. Also add helper method OutcomePayoutPoint.toTlvPoint
  • Loading branch information
Christewart committed Jan 16, 2021
1 parent f3e81d0 commit abc1fdd
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,13 @@ case class OracleRoutes(oracle: DLCOracle)(implicit
}
outcomes.map(num => Num(num.toDouble))
case decomp: DigitDecompositionEventDescriptorV0TLV =>
val sign = if (decomp.isSigned) {
Vector(Str("+"), Str("-"))
} else {
Vector.empty
val sign = decomp match {
case _: UnsignedDigitDecompositionEventDescriptor =>
Vector.empty
case _: SignedDigitDecompositionEventDescriptor =>
Vector(Str("+"), Str("-"))
}

val digits = 0.until(decomp.numDigits.toInt).map { _ =>
0
.until(decomp.base.toInt)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,12 +293,7 @@ object DLCMessage {
}

override lazy val toTLV: ContractInfoV1TLV = {
val tlvPoints = outcomeValueFunc.points.map { point =>
TLVPoint(point.outcome,
point.roundedPayout,
point.extraPrecision,
point.isEndpoint)
}
val tlvPoints = outcomeValueFunc.points.map(_.toTlvPoint)

ContractInfoV1TLV(base, numDigits, totalCollateral, tlvPoints)
}
Expand All @@ -312,6 +307,7 @@ object DLCMessage {
val points = tlv.points.map { point =>
val payoutWithPrecision =
point.value.toLong + (BigDecimal(point.extraPrecision) / (1 << 16))

OutcomePayoutPoint(point.outcome, payoutWithPrecision, point.isEndpoint)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.bitcoins.core.protocol.dlc

import org.bitcoins.core.currency.Satoshis
import org.bitcoins.core.protocol.tlv.TLVPoint
import org.bitcoins.core.util.{Indexed, NumberUtil}

import scala.math.BigDecimal.RoundingMode
Expand All @@ -15,15 +16,26 @@ case class DLCPayoutCurve(points: Vector[OutcomePayoutPoint]) {
/** These points (and their indices in this.points) represent the endpoints
* between which interpolation happens.
* In other words these endpoints define the pieces of the piecewise function.
*
* It's important to note that the index returned here is relative to the _entire_
* set of points, not the index relative to the set of endpoints.
*/
lazy val endpoints: Vector[Indexed[OutcomePayoutPoint]] =
Indexed(points).filter(_.element.isEndpoint)
lazy val endpoints: Vector[Indexed[OutcomePayoutEndpoint]] = {
val endpoints = points.zipWithIndex.collect {
case (o: OutcomePayoutEndpoint, idx) => (o, idx)
}
Indexed.fromGivenIndex(endpoints)
}

/** This Vector contains the function pieces between the endpoints */
lazy val functionComponents: Vector[DLCPayoutCurveComponent] = {
endpoints.init.zip(endpoints.tail).map { // All pairs of adjacent endpoints
val zipped: Vector[
(Indexed[OutcomePayoutEndpoint], Indexed[OutcomePayoutEndpoint])] =
endpoints.init.zip(endpoints.tail)
zipped.map { // All pairs of adjacent endpoints
case (Indexed(_, index), Indexed(_, nextIndex)) =>
DLCPayoutCurveComponent(points.slice(index, nextIndex + 1))
val slice = points.slice(index, nextIndex + 1)
DLCPayoutCurveComponent(slice)
}
}

Expand Down Expand Up @@ -70,7 +82,13 @@ case class DLCPayoutCurve(points: Vector[OutcomePayoutPoint]) {
sealed trait OutcomePayoutPoint {
def outcome: Long
def payout: BigDecimal
def isEndpoint: Boolean

def isEndPoint: Boolean = {
this match {
case _: OutcomePayoutEndpoint => true
case _: OutcomePayoutMidpoint => false
}
}

def roundedPayout: Satoshis = {
Satoshis(payout.setScale(0, RoundingMode.FLOOR).toLongExact)
Expand All @@ -89,6 +107,22 @@ sealed trait OutcomePayoutPoint {
case OutcomePayoutMidpoint(_, _) => OutcomePayoutMidpoint(outcome, payout)
}
}

/** Converts our internal representation to a TLV that can be sent over the wire */
def toTlvPoint: TLVPoint = {
this match {
case _: OutcomePayoutEndpoint =>
TLVPoint(outcome = outcome,
value = roundedPayout,
extraPrecision = extraPrecision,
isEndpoint = true)
case _: OutcomePayoutMidpoint =>
TLVPoint(outcome = outcome,
value = roundedPayout,
extraPrecision = extraPrecision,
isEndpoint = false)
}
}
}

object OutcomePayoutPoint {
Expand All @@ -114,7 +148,6 @@ object OutcomePayoutPoint {

case class OutcomePayoutEndpoint(outcome: Long, payout: BigDecimal)
extends OutcomePayoutPoint {
override val isEndpoint: Boolean = true

def toMidpoint: OutcomePayoutMidpoint = OutcomePayoutMidpoint(outcome, payout)
}
Expand All @@ -128,7 +161,6 @@ object OutcomePayoutEndpoint {

case class OutcomePayoutMidpoint(outcome: Long, payout: BigDecimal)
extends OutcomePayoutPoint {
override val isEndpoint: Boolean = false

def toEndpoint: OutcomePayoutEndpoint = OutcomePayoutEndpoint(outcome, payout)
}
Expand Down Expand Up @@ -179,9 +211,9 @@ sealed trait DLCPayoutCurveComponent {
object DLCPayoutCurveComponent {

def apply(points: Vector[OutcomePayoutPoint]): DLCPayoutCurveComponent = {
require(points.head.isEndpoint && points.last.isEndpoint,
require(points.head.isEndPoint && points.last.isEndPoint,
s"First and last points must be endpoints, $points")
require(points.tail.init.forall(!_.isEndpoint),
require(points.tail.init.forall(!_.isEndPoint),
s"Endpoint detected in middle, $points")

points match {
Expand Down Expand Up @@ -320,9 +352,10 @@ case class OutcomePayoutCubic(
/** A polynomial interpolating points and defining a piece of a larger payout curve */
case class OutcomePayoutPolynomial(points: Vector[OutcomePayoutPoint])
extends DLCPayoutCurveComponent {
require(points.head.isEndpoint && points.last.isEndpoint,
require(points.head.isInstanceOf[OutcomePayoutEndpoint] && points.last
.isInstanceOf[OutcomePayoutEndpoint],
s"First and last points must be endpoints, $points")
require(points.tail.init.forall(!_.isEndpoint),
require(points.tail.init.forall(!_.isInstanceOf[OutcomePayoutEndpoint]),
s"Endpoint detected in middle, $points")

override lazy val leftEndpoint: OutcomePayoutEndpoint =
Expand Down
71 changes: 44 additions & 27 deletions core/src/main/scala/org/bitcoins/core/protocol/tlv/TLV.scala
Original file line number Diff line number Diff line change
Expand Up @@ -603,32 +603,39 @@ sealed trait DigitDecompositionEventDescriptorV0TLV
require(numDigits > UInt16.zero,
s"Number of digits must be positive, got $numDigits")

/** Whether the outcome can be negative */
def isSigned: Boolean

/** The number of digits that the oracle will sign */
def numDigits: UInt16

override lazy val maxNum: BigInt = base.toBigInt.pow(numDigits.toInt) - 1

private lazy val maxDigit: NormalizedString = (base.toInt - 1).toString

override lazy val max: Vector[NormalizedString] = if (isSigned) {
NormalizedString("+") +: Vector.fill(numDigits.toInt)(maxDigit)
} else {
Vector.fill(numDigits.toInt)(maxDigit)
override lazy val max: Vector[NormalizedString] = {
this match {
case _: SignedDigitDecompositionEventDescriptor =>
NormalizedString("+") +: Vector.fill(numDigits.toInt)(maxDigit)
case _: UnsignedDigitDecompositionEventDescriptor =>
Vector.fill(numDigits.toInt)(maxDigit)

}
}

override lazy val minNum: BigInt = if (isSigned) {
-maxNum
} else {
0
override lazy val minNum: BigInt = {
this match {
case _: SignedDigitDecompositionEventDescriptor =>
-maxNum
case _: UnsignedDigitDecompositionEventDescriptor =>
0
}
}

override lazy val min: Vector[NormalizedString] = if (isSigned) {
NormalizedString("-") +: Vector.fill(numDigits.toInt)(maxDigit)
} else {
Vector.fill(numDigits.toInt)("0")
override lazy val min: Vector[NormalizedString] = {
this match {
case _: SignedDigitDecompositionEventDescriptor =>
NormalizedString("-") +: Vector.fill(numDigits.toInt)(maxDigit)
case _: UnsignedDigitDecompositionEventDescriptor =>
Vector.fill(numDigits.toInt)("0")
}
}

override lazy val step: UInt16 = UInt16.one
Expand All @@ -637,16 +644,26 @@ sealed trait DigitDecompositionEventDescriptorV0TLV
DigitDecompositionEventDescriptorV0TLV.tpe

override lazy val value: ByteVector = {
base.bytes ++
boolBytes(isSigned) ++
strBytes(unit) ++
val start = base.bytes
val signByte = this match {
case _: UnsignedDigitDecompositionEventDescriptor =>
boolBytes(false)
case _: SignedDigitDecompositionEventDescriptor =>
boolBytes(true)
}
val end = strBytes(unit) ++
precision.bytes ++
numDigits.bytes
start ++ signByte ++ end
}

override def noncesNeeded: Int = {
if (isSigned) numDigits.toInt + 1
else numDigits.toInt
this match {
case _: SignedDigitDecompositionEventDescriptor =>
numDigits.toInt + 1
case _: UnsignedDigitDecompositionEventDescriptor =>
numDigits.toInt
}
}
}

Expand All @@ -656,19 +673,15 @@ case class SignedDigitDecompositionEventDescriptor(
numDigits: UInt16,
unit: NormalizedString,
precision: Int32)
extends DigitDecompositionEventDescriptorV0TLV {
override val isSigned: Boolean = true
}
extends DigitDecompositionEventDescriptorV0TLV

/** Represents a large range event that is unsigned */
case class UnsignedDigitDecompositionEventDescriptor(
base: UInt16,
numDigits: UInt16,
unit: NormalizedString,
precision: Int32)
extends DigitDecompositionEventDescriptorV0TLV {
override val isSigned: Boolean = false
}
extends DigitDecompositionEventDescriptorV0TLV

object DigitDecompositionEventDescriptorV0TLV
extends TLVFactory[DigitDecompositionEventDescriptorV0TLV] {
Expand Down Expand Up @@ -895,7 +908,11 @@ object TLVPoint extends Factory[TLVPoint] {
val outcome = BigSizeUInt(bytes.tail)
val value = UInt64(bytes.drop(1 + outcome.byteSize).take(8))
val extraPrecision = UInt16(bytes.drop(9 + outcome.byteSize).take(2)).toInt
TLVPoint(outcome.toLong, Satoshis(value.toLong), extraPrecision, isEndpoint)

TLVPoint(outcome = outcome.toLong,
value = Satoshis(value.toLong),
extraPrecision = extraPrecision,
isEndpoint = isEndpoint)
}
}

Expand Down
12 changes: 11 additions & 1 deletion core/src/main/scala/org/bitcoins/core/util/Indexed.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
package org.bitcoins.core.util

case class Indexed[T](element: T, index: Int)
case class Indexed[+T](element: T, index: Int)

object Indexed {

def apply[T](vec: Vector[T]): Vector[Indexed[T]] = {
vec.zipWithIndex.map { case (elem, index) => Indexed(elem, index) }
}

/** Takes in a given vector of T's with their corresponding index
* and returns a Vector[Indexed[T]].
*
* This is useful in situations where you want to preserve the initial
* index in a set of elements, but have performed subsequent collection operations (like .filter, .filterNot, .collect etc)
*/
def fromGivenIndex[T](vec: Vector[(T, Int)]): Vector[Indexed[T]] = {
vec.map { case (t, idx) => Indexed(t, idx) }
}
}
23 changes: 14 additions & 9 deletions dlc-oracle/src/main/scala/org/bitcoins/dlc/oracle/DLCOracle.scala
Original file line number Diff line number Diff line change
Expand Up @@ -300,12 +300,14 @@ case class DLCOracle(private val extPrivateKey: ExtPrivateKeyHardened)(implicit
oracleEventTLV: OracleEventTLV,
num: Long): Future[OracleEvent] = {

val eventDescriptorTLV = oracleEventTLV.eventDescriptor match {
case _: EnumEventDescriptorV0TLV | _: RangeEventDescriptorV0TLV =>
throw new IllegalArgumentException(
"Must have a DigitDecomposition event descriptor use signEvent instead")
case decomp: DigitDecompositionEventDescriptorV0TLV =>
decomp
val eventDescriptorTLV: DigitDecompositionEventDescriptorV0TLV = {
oracleEventTLV.eventDescriptor match {
case _: EnumEventDescriptorV0TLV | _: RangeEventDescriptorV0TLV =>
throw new IllegalArgumentException(
"Must have a DigitDecomposition event descriptor use signEvent instead")
case decomp: DigitDecompositionEventDescriptorV0TLV =>
decomp
}
}

// Make this a vec so it is easier to add on
Expand Down Expand Up @@ -338,9 +340,12 @@ case class DLCOracle(private val extPrivateKey: ExtPrivateKeyHardened)(implicit
eventDescriptorTLV.base.toInt,
eventDescriptorTLV.numDigits.toInt)

val nonces =
if (eventDescriptorTLV.isSigned) oracleEventTLV.nonces.tail
else oracleEventTLV.nonces
val nonces = eventDescriptorTLV match {
case _: UnsignedDigitDecompositionEventDescriptor =>
oracleEventTLV.nonces
case _: SignedDigitDecompositionEventDescriptor =>
oracleEventTLV.nonces.tail
}

val digitSigFs = nonces.zipWithIndex.map {
case (nonce, index) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,12 @@ trait EventDbUtil {
Vector.empty
}

val digitNonces = if (decomp.isSigned) nonces.tail else nonces
val digitNonces = decomp match {
case _: UnsignedDigitDecompositionEventDescriptor =>
nonces
case _: SignedDigitDecompositionEventDescriptor =>
nonces.tail
}

val digitDbs = digitNonces.flatMap { nonce =>
0.until(decomp.base.toInt).map { num =>
Expand Down

0 comments on commit abc1fdd

Please sign in to comment.