mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-19 21:45:36 +01:00
2021 01 15 dlc refactors (#2518)
* Kill DigitDecomp.isSigned * Create TLVEndpoint, TLVMidpoint ADT. Also add helper method OutcomePayoutPoint.toTlvPoint
This commit is contained in:
parent
f3e81d027d
commit
abc1fdd23f
7 changed files with 127 additions and 59 deletions
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 =
|
||||
|
|
|
@ -603,9 +603,6 @@ 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
|
||||
|
||||
|
@ -613,22 +610,32 @@ sealed trait DigitDecompositionEventDescriptorV0TLV
|
|||
|
||||
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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -656,9 +673,7 @@ 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(
|
||||
|
@ -666,9 +681,7 @@ case class UnsignedDigitDecompositionEventDescriptor(
|
|||
numDigits: UInt16,
|
||||
unit: NormalizedString,
|
||||
precision: Int32)
|
||||
extends DigitDecompositionEventDescriptorV0TLV {
|
||||
override val isSigned: Boolean = false
|
||||
}
|
||||
extends DigitDecompositionEventDescriptorV0TLV
|
||||
|
||||
object DigitDecompositionEventDescriptorV0TLV
|
||||
extends TLVFactory[DigitDecompositionEventDescriptorV0TLV] {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -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) =>
|
||||
|
|
|
@ -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 =>
|
||||
|
|
Loading…
Add table
Reference in a new issue