1
0
Fork 0
mirror of https://github.com/bitcoin-s/bitcoin-s.git synced 2025-03-21 06:22:21 +01:00

Restructure Contract and Oracle Info ()

* Rewrote all oracle and contract info code

* Fixed src compilation

* For real this time

* Tests compiling (except for Lloyd example)

* Fixed all non-json dlc tests

* Regenerated test vectors

* Fix dlc wallet

* Fix TLV verfiy sig func for Oracle Tests

* Fix migrations tests

Co-authored-by: benthecarman <benthecarman@live.com>
This commit is contained in:
Nadav Kohen 2021-01-11 14:05:44 -06:00 committed by GitHub
parent f06dcbb160
commit d8b0c21f20
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 3363 additions and 3263 deletions
app-commons/src/main/scala/org/bitcoins/commons/serializers
app
cli/src/main/scala/org/bitcoins/cli
gui/src/main/scala/org/bitcoins/gui/dlc
server-test/src/test/scala/org/bitcoins/server
server/src/main/scala/org/bitcoins/server
core-test/src/test/scala/org/bitcoins/core/protocol/dlc
core/src/main/scala/org/bitcoins/core/protocol
db-commons-test/src/test/scala/org/bitcoins/db
db-commons/src/main/scala/org/bitcoins/db
dlc-test/src/test/scala/org/bitcoins/dlc
dlc-wallet-test/src/test/scala/org/bitcoins/dlc/wallet
dlc-wallet/src/main
dlc/src/main/scala/org/bitcoins/dlc
testkit/src/main/scala/org/bitcoins/testkit

View file

@ -101,8 +101,8 @@ object Picklers {
implicit val contractInfoPickler: ReadWriter[ContractInfo] =
readwriter[String].bimap(_.hex, ContractInfo.fromHex)
implicit val contractInfoTLVPickler: ReadWriter[ContractInfoTLV] =
readwriter[String].bimap(_.hex, ContractInfoTLV.fromHex)
implicit val contractInfoTLVPickler: ReadWriter[ContractInfoV0TLV] =
readwriter[String].bimap(_.hex, ContractInfoV0TLV.fromHex)
implicit val schnorrDigitalSignaturePickler: ReadWriter[
SchnorrDigitalSignature] =
@ -164,7 +164,6 @@ object Picklers {
"paramHash" -> Str(paramHash.hex),
"isInitiator" -> Bool(isInitiator),
"tempContractId" -> Str(tempContractId.hex),
"oracleInfo" -> Str(oracleInfo.hex),
"contractInfo" -> Str(contractInfo.hex),
"contractMaturity" -> Num(
timeouts.contractMaturity.toUInt32.toLong.toDouble),
@ -185,7 +184,6 @@ object Picklers {
"isInitiator" -> Bool(isInitiator),
"tempContractId" -> Str(tempContractId.hex),
"contractId" -> Str(contractId.toHex),
"oracleInfo" -> Str(oracleInfo.hex),
"contractInfo" -> Str(contractInfo.hex),
"contractMaturity" -> Num(
timeouts.contractMaturity.toUInt32.toLong.toDouble),
@ -206,7 +204,6 @@ object Picklers {
"isInitiator" -> Bool(isInitiator),
"tempContractId" -> Str(tempContractId.hex),
"contractId" -> Str(contractId.toHex),
"oracleInfo" -> Str(oracleInfo.hex),
"contractInfo" -> Str(contractInfo.hex),
"contractMaturity" -> Num(
timeouts.contractMaturity.toUInt32.toLong.toDouble),
@ -228,7 +225,6 @@ object Picklers {
"isInitiator" -> Bool(isInitiator),
"tempContractId" -> Str(tempContractId.hex),
"contractId" -> Str(contractId.toHex),
"oracleInfo" -> Str(oracleInfo.hex),
"contractInfo" -> Str(contractInfo.hex),
"contractMaturity" -> Num(
timeouts.contractMaturity.toUInt32.toLong.toDouble),
@ -251,7 +247,6 @@ object Picklers {
"isInitiator" -> Bool(isInitiator),
"tempContractId" -> Str(tempContractId.hex),
"contractId" -> Str(contractId.toHex),
"oracleInfo" -> Str(oracleInfo.hex),
"contractInfo" -> Str(contractInfo.hex),
"contractMaturity" -> Num(
timeouts.contractMaturity.toUInt32.toLong.toDouble),
@ -280,7 +275,6 @@ object Picklers {
"isInitiator" -> Bool(isInitiator),
"tempContractId" -> Str(tempContractId.hex),
"contractId" -> Str(contractId.toHex),
"oracleInfo" -> Str(oracleInfo.hex),
"contractInfo" -> Str(contractInfo.hex),
"contractMaturity" -> Num(
timeouts.contractMaturity.toUInt32.toLong.toDouble),
@ -313,7 +307,6 @@ object Picklers {
"isInitiator" -> Bool(isInitiator),
"tempContractId" -> Str(tempContractId.hex),
"contractId" -> Str(contractId.toHex),
"oracleInfo" -> Str(oracleInfo.hex),
"contractInfo" -> Str(contractInfo.hex),
"contractMaturity" -> Num(
timeouts.contractMaturity.toUInt32.toLong.toDouble),
@ -338,7 +331,6 @@ object Picklers {
"isInitiator" -> Bool(isInitiator),
"tempContractId" -> Str(tempContractId.hex),
"contractId" -> Str(contractId.toHex),
"oracleInfo" -> Str(oracleInfo.hex),
"contractInfo" -> Str(contractInfo.hex),
"contractMaturity" -> Num(
timeouts.contractMaturity.toUInt32.toLong.toDouble),
@ -367,8 +359,7 @@ object Picklers {
val state = DLCState.fromString(obj("state").str)
val isInitiator = obj("isInitiator").bool
val tempContractId = Sha256Digest(obj("tempContractId").str)
val oracleInfo = OracleInfo(obj("oracleInfo").str)
val contractInfoTLV = ContractInfoTLV(obj("contractInfo").str)
val contractInfoTLV = ContractInfoV0TLV(obj("contractInfo").str)
val contractMaturity =
BlockStamp(UInt32(obj("contractMaturity").num.toLong))
val contractTimeout = BlockStamp(UInt32(obj("contractTimeout").num.toLong))
@ -398,7 +389,6 @@ object Picklers {
paramHash,
isInitiator,
tempContractId,
oracleInfo,
ContractInfo.fromTLV(contractInfoTLV),
DLCTimeouts(contractMaturity, contractTimeout),
feeRate,
@ -411,7 +401,6 @@ object Picklers {
isInitiator,
tempContractId,
contractId,
oracleInfo,
ContractInfo.fromTLV(contractInfoTLV),
DLCTimeouts(contractMaturity, contractTimeout),
feeRate,
@ -424,7 +413,6 @@ object Picklers {
isInitiator,
tempContractId,
contractId,
oracleInfo,
ContractInfo.fromTLV(contractInfoTLV),
DLCTimeouts(contractMaturity, contractTimeout),
feeRate,
@ -437,7 +425,6 @@ object Picklers {
isInitiator,
tempContractId,
contractId,
oracleInfo,
ContractInfo.fromTLV(contractInfoTLV),
DLCTimeouts(contractMaturity, contractTimeout),
feeRate,
@ -451,7 +438,6 @@ object Picklers {
isInitiator,
tempContractId,
contractId,
oracleInfo,
ContractInfo.fromTLV(contractInfoTLV),
DLCTimeouts(contractMaturity, contractTimeout),
feeRate,
@ -465,7 +451,6 @@ object Picklers {
isInitiator,
tempContractId,
contractId,
oracleInfo,
ContractInfo.fromTLV(contractInfoTLV),
DLCTimeouts(contractMaturity, contractTimeout),
feeRate,
@ -484,7 +469,6 @@ object Picklers {
isInitiator,
tempContractId,
contractId,
oracleInfo,
ContractInfo.fromTLV(contractInfoTLV),
DLCTimeouts(contractMaturity, contractTimeout),
feeRate,
@ -501,7 +485,6 @@ object Picklers {
isInitiator,
tempContractId,
contractId,
oracleInfo,
ContractInfo.fromTLV(contractInfoTLV),
DLCTimeouts(contractMaturity, contractTimeout),
feeRate,

View file

@ -176,10 +176,10 @@ object CliReaders {
val reads: String => ContractInfo = ContractInfo.fromHex
}
implicit val contractInfoTLVReads: Read[ContractInfoTLV] =
new Read[ContractInfoTLV] {
implicit val contractInfoTLVReads: Read[ContractInfoV0TLV] =
new Read[ContractInfoV0TLV] {
val arity: Int = 1
val reads: String => ContractInfoTLV = ContractInfoTLV.fromHex
val reads: String => ContractInfoV0TLV = ContractInfoV0TLV.fromHex
}
implicit val blockStampReads: Read[BlockStamp] =

View file

@ -6,7 +6,6 @@ import java.time.Instant
import org.bitcoins.cli.CliCommand._
import org.bitcoins.cli.CliReaders._
import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.LockUnspentOutputParameter
import org.bitcoins.core.protocol.dlc.DLCMessage._
import org.bitcoins.commons.serializers.Picklers._
import org.bitcoins.core.api.wallet.CoinSelectionAlgo
import org.bitcoins.core.config.NetworkParameters
@ -164,23 +163,14 @@ object ConsoleCli {
cmd("createdlcoffer")
.action((_, conf) =>
conf.copy(
command = CreateDLCOffer(OracleAnnouncementV0TLV.dummy,
ContractInfo.empty.toTLV,
command = CreateDLCOffer(ContractInfoV0TLV.dummy,
Satoshis.zero,
None,
UInt32.zero,
UInt32.zero)))
.text("Creates a DLC offer that another party can accept")
.children(
arg[OracleAnnouncementTLV]("oracle")
.required()
.action((oracle, conf) =>
conf.copy(command = conf.command match {
case offer: CreateDLCOffer =>
offer.copy(oracle = oracle)
case other => other
})),
arg[ContractInfoTLV]("contractInfo")
arg[ContractInfoV0TLV]("contractInfo")
.required()
.action((info, conf) =>
conf.copy(command = conf.command match {
@ -1381,8 +1371,7 @@ object ConsoleCli {
case GetDLCs => RequestParam("getdlcs")
case GetDLC(paramHash) =>
RequestParam("getdlc", Seq(up.writeJs(paramHash)))
case CreateDLCOffer(oracle,
contractInfo,
case CreateDLCOffer(contractInfo,
collateral,
feeRateOpt,
locktime,
@ -1390,7 +1379,6 @@ object ConsoleCli {
RequestParam(
"createdlcoffer",
Seq(
up.writeJs(oracle),
up.writeJs(contractInfo),
up.writeJs(collateral),
up.writeJs(feeRateOpt),
@ -1712,8 +1700,7 @@ object CliCommand {
// DLC
case class CreateDLCOffer(
oracle: OracleAnnouncementTLV,
contractInfo: ContractInfoTLV,
contractInfo: ContractInfoV0TLV,
collateral: Satoshis,
feeRateOpt: Option[SatoshisPerVirtualByte],
locktime: UInt32,

View file

@ -4,7 +4,7 @@ import org.bitcoins.cli.CliCommand._
import org.bitcoins.cli.{CliCommand, Config, ConsoleCli}
import org.bitcoins.commons.serializers.Picklers._
import org.bitcoins.core.config.MainNet
import org.bitcoins.core.number.{Int32, UInt16, UInt32}
import org.bitcoins.core.currency.Satoshis
import org.bitcoins.core.protocol.dlc.DLCMessage._
import org.bitcoins.core.protocol.dlc.DLCStatus
import org.bitcoins.core.protocol.tlv._
@ -118,65 +118,51 @@ class DLCPaneModel(resultArea: TextArea, oracleInfoArea: TextArea) {
}
result match {
case Some(contractInfo) =>
case Some(contractDescriptor) =>
val builder = new StringBuilder()
builder.append(s"Serialized Contract Info:\n${contractInfo.hex}\n\n")
val privKey = ECPrivateKey.freshPrivateKey
val pubKey = privKey.schnorrPublicKey
val (kValues, rValues, oracleInfo) = contractInfo match {
case SingleNonceContractInfo(_) =>
val (kValues, oracleInfo) = contractDescriptor match {
case EnumContractDescriptor(events) =>
val kValue = ECPrivateKey.freshPrivateKey
val rValue = kValue.schnorrNonce
val oracleInfo = SingleNonceOracleInfo(pubKey, rValue)
val oracleInfo = EnumSingleOracleInfo(
OracleAnnouncementV0TLV
.dummyForEventsAndKeys(privKey, rValue, events.map(_._1)))
(Vector(kValue), Vector(rValue), oracleInfo)
case MultiNonceContractInfo(_, _, numDigits, _, _) =>
(Vector(kValue), oracleInfo)
case (_, NumericContractDescriptor(_, numDigits, _)) =>
val kValues =
0.until(numDigits).map(_ => ECPrivateKey.freshPrivateKey).toVector
val rValues = kValues.map(_.schnorrNonce)
val oracleInfo = MultiNonceOracleInfo(pubKey, rValues)
val oracleInfo = NumericSingleOracleInfo(
OracleAnnouncementV0TLV.dummyForKeys(privKey, rValues))
(kValues, rValues, oracleInfo)
(kValues, oracleInfo)
}
val contractInfo = contractDescriptor match {
case descriptor: EnumContractDescriptor =>
ContractInfo(descriptor, oracleInfo.asInstanceOf[EnumOracleInfo])
case (totalCollateral: Satoshis,
descriptor: NumericContractDescriptor) =>
ContractInfo(totalCollateral, descriptor, oracleInfo)
}
builder.append(s"Serialized Contract Info:\n${contractInfo.hex}\n\n")
if (GlobalData.network != MainNet) {
val descriptor = contractInfo match {
case SingleNonceContractInfo(outcomeValueMap) =>
EnumEventDescriptorV0TLV(outcomeValueMap.map(_._1.outcome))
case MultiNonceContractInfo(_, base, numDigits, _, _) =>
UnsignedDigitDecompositionEventDescriptor(UInt16(base),
UInt16(numDigits),
"units",
Int32.zero)
}
val oracleEvent = OracleEventV0TLV(oracleInfo.nonces,
UInt32.zero,
descriptor,
"dummy oracle")
val announcementSig =
privKey.schnorrSign(
CryptoUtil
.sha256DLCAnnouncement(oracleEvent.bytes)
.bytes)
val announcement =
OracleAnnouncementV0TLV(announcementSig, pubKey, oracleEvent)
builder.append(
s"Oracle Public Key: ${oracleInfo.publicKey.hex}\nEvent R values: ${oracleInfo.nonces.map(_.hex).mkString(",")}\n\n")
builder.append(
s"Oracle Public Key: ${pubKey.hex}\nEvent R values: ${rValues.map(_.hex).mkString(",")}\n\n")
s"Serialized Oracle Announcement: ${oracleInfo.announcement.hex}\n\n")
builder.append(
s"Serialized Oracle Announcement: ${announcement.hex}\n\n")
contractInfo match {
case contractInfo: SingleNonceContractInfo =>
contractInfo.contractDescriptor match {
case descriptor: EnumContractDescriptor =>
builder.append("Outcomes and oracle sigs in order of entry:\n")
contractInfo.keys.foreach { outcome =>
descriptor.keys.foreach { outcome =>
val bytes = outcome.serialized.head
val hash = CryptoUtil
.sha256DLCAttestation(bytes)
@ -184,10 +170,10 @@ class DLCPaneModel(resultArea: TextArea, oracleInfoArea: TextArea) {
val sig = privKey.schnorrSignWithNonce(hash, kValues.head)
builder.append(s"$outcome - ${sig.hex}\n")
}
case contractInfo: MultiNonceContractInfo =>
case _: NumericContractDescriptor =>
builder.append("Oracle sigs:\n")
val sortedOutcomes = contractInfo.outcomeVec.sortBy(_._2)
val sortedOutcomes = contractInfo.outcomeVecOpt.get.sortBy(_._2)
val max = UnsignedNumericOutcome(sortedOutcomes.last._1)
val middle = UnsignedNumericOutcome(
@ -231,7 +217,7 @@ class DLCPaneModel(resultArea: TextArea, oracleInfoArea: TextArea) {
builder.append(s"remote win sigs - $minSigsStr")
}
GlobalDLCData.lastOracleAnnouncement = announcement.hex
GlobalDLCData.lastOracleAnnouncement = oracleInfo.announcement.hex
}
GlobalDLCData.lastContractInfo = contractInfo.hex

View file

@ -1,7 +1,8 @@
package org.bitcoins.gui.dlc
import org.bitcoins.core.protocol.dlc.{AcceptedDLCStatus, DLCStatus}
import org.bitcoins.core.protocol.dlc.DLCMessage.SingleOracleInfo
import org.bitcoins.core.protocol.dlc.DLCStatus._
import org.bitcoins.core.protocol.dlc.{AcceptedDLCStatus, DLCStatus}
import scalafx.beans.property.StringProperty
import scalafx.geometry.Insets
import scalafx.scene.control.{ContextMenu, MenuItem, TableColumn, TableView}
@ -68,7 +69,11 @@ class DLCTableView(model: DLCPaneModel) {
text = "Oracle"
prefWidth = 150
cellValueFactory = { status =>
new StringProperty(status, "Oracle", status.value.oracleInfo.pubKey.hex)
new StringProperty(
status,
"Oracle",
status.value.oracleInfo.asInstanceOf[SingleOracleInfo].publicKey.hex
) // FIXME
}
}
@ -76,10 +81,14 @@ class DLCTableView(model: DLCPaneModel) {
text = "Event"
prefWidth = 150
cellValueFactory = { status =>
new StringProperty(
status,
"Event",
status.value.oracleInfo.nonces.map(_.hex).mkString(""))
new StringProperty(status,
"Event",
status.value.oracleInfo
.asInstanceOf[SingleOracleInfo]
.nonces
.map(_.hex)
.mkString("")
) // FIXME
}
}

View file

@ -1,7 +1,7 @@
package org.bitcoins.gui.dlc.dialog
import org.bitcoins.cli.CliCommand._
import org.bitcoins.core.protocol.dlc.DLCMessage.OracleInfo
import org.bitcoins.core.protocol.dlc.DLCMessage.{OracleInfo, SingleOracleInfo}
import org.bitcoins.core.protocol.tlv._
import scalafx.scene.Node
import scalafx.scene.control.Alert.AlertType
@ -30,8 +30,8 @@ class AcceptDLCDialog
def validateMatchingAnnouncement(
offer: LnMessage[DLCOfferTLV],
announcement: OracleAnnouncementTLV): Boolean = {
val fromOffer = OracleInfo.fromTLV(offer.tlv.oracleInfo)
val fromAnnouncement = OracleInfo.fromOracleAnnouncement(announcement)
val fromOffer = OracleInfo.fromTLV(offer.tlv.contractInfo.oracleInfo)
val fromAnnouncement = SingleOracleInfo(announcement)
fromOffer == fromAnnouncement
}

View file

@ -1,7 +1,7 @@
package org.bitcoins.gui.dlc.dialog
import org.bitcoins.core.protocol.dlc.DLCMessage.SingleNonceContractInfo
import org.bitcoins.core.currency.Satoshis
import org.bitcoins.core.protocol.dlc.DLCMessage.EnumContractDescriptor
import org.bitcoins.core.protocol.tlv.EnumOutcome
import org.bitcoins.gui.GlobalData
import org.bitcoins.gui.util.GUIUtil.setNumericInput
@ -15,9 +15,9 @@ import scala.collection._
object InitEnumContractDialog {
def showAndWait(parentWindow: Window): Option[SingleNonceContractInfo] = {
def showAndWait(parentWindow: Window): Option[EnumContractDescriptor] = {
val dialog =
new Dialog[Option[SingleNonceContractInfo]]() {
new Dialog[Option[EnumContractDescriptor]]() {
initOwner(parentWindow)
title = "Initialize Demo Oracle"
headerText = "Enter contract outcomes and their outcome values"
@ -87,13 +87,13 @@ object InitEnumContractDialog {
EnumOutcome(str) -> Satoshis(BigInt(value))
}.toVector
Some(SingleNonceContractInfo(contractMap))
Some(EnumContractDescriptor(contractMap))
} else None
val result = dialog.showAndWait()
result match {
case Some(Some(contractInfo: SingleNonceContractInfo)) =>
case Some(Some(contractInfo: EnumContractDescriptor)) =>
Some(contractInfo)
case Some(_) | None => None
}

View file

@ -1,7 +1,7 @@
package org.bitcoins.gui.dlc.dialog
import org.bitcoins.core.protocol.dlc.DLCMessage.MultiNonceContractInfo
import org.bitcoins.core.currency.Satoshis
import org.bitcoins.core.protocol.dlc.DLCMessage.NumericContractDescriptor
import org.bitcoins.core.protocol.dlc.{
DLCPayoutCurve,
OutcomePayoutPoint,
@ -22,9 +22,10 @@ import scala.util.{Failure, Success, Try}
object InitNumericContractDialog {
def showAndWait(parentWindow: Window): Option[MultiNonceContractInfo] = {
def showAndWait(
parentWindow: Window): Option[(Satoshis, NumericContractDescriptor)] = {
val dialog =
new Dialog[Option[MultiNonceContractInfo]]() {
new Dialog[Option[(Satoshis, NumericContractDescriptor)]]() {
initOwner(parentWindow)
title = "Initialize Demo Oracle"
headerText = "Enter contract interpolation points"
@ -151,9 +152,8 @@ object InitNumericContractDialog {
onAction = _ => addRoundingRow()
}
def getContractInfo: Try[MultiNonceContractInfo] = {
def getContractInfo: Try[(Satoshis, NumericContractDescriptor)] = {
Try {
val base = baseTF.text.value.toInt
val numDigits = numDigitsTF.text.value.toInt
val totalCollateral = Satoshis(totalCollateralTF.text.value.toLong)
@ -186,11 +186,10 @@ object InitNumericContractDialog {
require(sorted == outcomesValuePoints, "Must be sorted by outcome")
val func = DLCPayoutCurve(outcomesValuePoints)
MultiNonceContractInfo(func,
base,
numDigits,
totalCollateral,
RoundingIntervals(roundingIntervalsStarts))
(totalCollateral,
NumericContractDescriptor(func,
numDigits,
RoundingIntervals(roundingIntervalsStarts)))
}
}
@ -274,13 +273,12 @@ object InitNumericContractDialog {
onAction = _ => {
getContractInfo match {
case Failure(_) => ()
case Success(contractInfo) =>
DLCPlotUtil.plotCETsWithOriginalCurve(
contractInfo.base,
contractInfo.numDigits,
contractInfo.outcomeValueFunc,
contractInfo.totalCollateral,
getRoundingIntervals)
case Success((totalCollateral, descriptor)) =>
DLCPlotUtil.plotCETsWithOriginalCurve(base = 2,
descriptor.numDigits,
descriptor.outcomeValueFunc,
totalCollateral,
getRoundingIntervals)
()
}
}
@ -314,8 +312,11 @@ object InitNumericContractDialog {
} else None
dialog.showAndWait() match {
case Some(Some(contractInfo: MultiNonceContractInfo)) =>
Some(contractInfo)
case Some(
Some(
(totalCollateral: Satoshis,
descriptor: NumericContractDescriptor))) =>
Some((totalCollateral, descriptor))
case Some(_) | None => None
}
}

View file

@ -26,10 +26,8 @@ class OfferDLCDialog
}
CreateDLCOffer(
oracle = OracleAnnouncementV0TLV.fromHex(
readStringFromNode(inputs(oracleAnnouncementStr))),
contractInfo =
ContractInfoTLV.fromHex(readStringFromNode(inputs(contractInfoStr))),
ContractInfoV0TLV.fromHex(readStringFromNode(inputs(contractInfoStr))),
collateral = Satoshis(BigInt(readStringFromNode(inputs(collateralStr)))),
feeRateOpt = feeRate,
locktime = UInt32(BigInt(readStringFromNode(inputs(locktimeStr)))),

View file

@ -1,8 +1,8 @@
package org.bitcoins.gui.dlc.dialog
import org.bitcoins.core.protocol.dlc.DLCMessage.{
MultiNonceContractInfo,
SingleNonceContractInfo
EnumContractDescriptor,
NumericContractDescriptor
}
import org.bitcoins.core.protocol.dlc.{DLCPayoutCurve, DLCStatus}
import org.bitcoins.gui.GlobalData
@ -184,28 +184,27 @@ object ViewDLCDialog {
add(node, columnIndex = 1, rowIndex = row)
row += 1
status.contractInfo match {
case _: SingleNonceContractInfo => ()
case MultiNonceContractInfo(outcomeValueFunc,
base,
numDigits,
totalCollateral,
roundingIntervals) =>
status.contractInfo.contractDescriptor match {
case _: EnumContractDescriptor => ()
case NumericContractDescriptor(outcomeValueFunc,
numDigits,
roundingIntervals) =>
val previewGraphButton: Button = new Button("Preview Graph") {
onAction = _ => {
val payoutCurve = if (status.isInitiator) {
outcomeValueFunc
} else {
DLCPayoutCurve(outcomeValueFunc.points.map { point =>
point.copy(payout = totalCollateral.toLong - point.payout)
point.copy(payout =
status.totalCollateral.satoshis.toLong - point.payout)
})
}
DLCPlotUtil.plotCETsWithOriginalCurve(base,
numDigits,
payoutCurve,
totalCollateral,
roundingIntervals)
DLCPlotUtil.plotCETsWithOriginalCurve(
base = 2,
numDigits,
payoutCurve,
status.contractInfo.totalCollateral,
roundingIntervals)
()
}
}

View file

@ -806,14 +806,12 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
Vector("ffbbcde836cee437a2fa4ef7db1ea3d79ca71c0c821d2a197dda51bc6534f562",
"e770f42c578084a4a096ce1085f7fe508f8d908d2c5e6e304b2c3eab9bc973ea")
val contractInfo = SingleNonceContractInfo.fromStringVec(
val contractDesc = EnumContractDescriptor.fromStringVec(
Vector(
(contractInfoDigests.head, Satoshis(5)),
(contractInfoDigests.last, Satoshis(4))
))
val contractInfoTLV = contractInfo.toTLV
val contractMaturity = 1580323752
val contractTimeout = 1581323752
@ -859,11 +857,13 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
val announcementTLV = OracleAnnouncementV0TLV(
"fdd8249426cbca0e5366f6688fd837a83d3fe34d103f8d88a3bdc40d648e47e43c6f70a7e65fb85d5de46779604b541ebe74d06b3c316446a6f97fcd23d6de8e1d7b9451f74577f8cab8361962ce642a8da4b1f48f8813ed243203cb50ebba45c789abf0fdd8223000015b0fb6b85a9badee0a826349822db7412f79c71efdd903eac94a10ee10d6e4425fe3da00fdd80604000101620161")
val oracleInfo = SingleNonceOracleInfo(announcementTLV.publicKey,
announcementTLV.eventTLV.nonces.head)
val oracleInfo = EnumSingleOracleInfo(announcementTLV)
val contractInfo = ContractInfo(contractDesc, oracleInfo)
val contractInfoTLV = contractInfo.toTLV
val offer = DLCOffer(
OracleAndContractInfo(oracleInfo, contractInfo),
contractInfo,
dummyDLCKeys,
Satoshis(2500),
Vector(fundingInput, fundingInput),
@ -874,14 +874,12 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
"create a dlc offer" in {
(mockWalletApi
.createDLCOffer(_: OracleInfo,
_: ContractInfoTLV,
.createDLCOffer(_: ContractInfoV0TLV,
_: Satoshis,
_: Option[FeeUnit],
_: UInt32,
_: UInt32))
.expects(
oracleInfo,
contractInfoTLV,
Satoshis(2500),
Some(SatoshisPerVirtualByte(Satoshis.one)),
@ -894,7 +892,6 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
ServerCommand(
"createdlcoffer",
Arr(
Str(announcementTLV.hex),
Str(contractInfoTLV.hex),
Num(2500),
Num(1),

View file

@ -628,8 +628,7 @@ object GetDLC extends ServerJsonModels {
}
case class CreateDLCOffer(
announcement: OracleAnnouncementTLV,
contractInfoTLV: ContractInfoTLV,
contractInfoTLV: ContractInfoV0TLV,
collateral: Satoshis,
feeRateOpt: Option[SatoshisPerVirtualByte],
locktime: UInt32,
@ -640,16 +639,14 @@ object CreateDLCOffer extends ServerJsonModels {
def fromJsArr(jsArr: ujson.Arr): Try[CreateDLCOffer] = {
jsArr.arr.toList match {
case announcementJs :: contractInfoJs :: collateralJs :: feeRateOptJs :: locktimeJs :: refundLTJs :: Nil =>
case contractInfoJs :: collateralJs :: feeRateOptJs :: locktimeJs :: refundLTJs :: Nil =>
Try {
val announcement = jsToOracleAnnouncementTLV(announcementJs)
val contractInfoTLV = jsToContractInfoTLV(contractInfoJs)
val collateral = jsToSatoshis(collateralJs)
val feeRate = jsToSatoshisPerVirtualByteOpt(feeRateOptJs)
val locktime = jsToUInt32(locktimeJs)
val refundLT = jsToUInt32(refundLTJs)
CreateDLCOffer(announcement,
contractInfoTLV,
CreateDLCOffer(contractInfoTLV,
collateral,
feeRate,
locktime,
@ -1215,10 +1212,10 @@ trait ServerJsonModels {
"Expected an OracleAnnouncementTLV as a hex string")
}
def jsToContractInfoTLV(js: Value): ContractInfoTLV =
def jsToContractInfoTLV(js: Value): ContractInfoV0TLV =
js match {
case str: Str =>
ContractInfoTLV(str.value)
ContractInfoV0TLV(str.value)
case _: Value =>
throw Value.InvalidData(js, "Expected a ContractInfo as a hex string")
}

View file

@ -8,7 +8,6 @@ import org.bitcoins.commons.serializers.Picklers._
import org.bitcoins.core.api.wallet.db.SpendingInfoDb
import org.bitcoins.core.currency._
import org.bitcoins.core.protocol.tlv._
import org.bitcoins.core.protocol.dlc.DLCMessage.OracleInfo
import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.wallet.utxo.{AddressLabelTagType, TxoState}
import org.bitcoins.crypto.NetworkElement
@ -19,10 +18,8 @@ import org.bitcoins.wallet.config.WalletAppConfig
import ujson._
import upickle.default._
import java.time.Instant
import java.nio.file.Files
import java.time.Instant
import scala.concurrent.Future
import scala.util.{Failure, Success}
@ -263,23 +260,25 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit
case Failure(exception) =>
reject(ValidationRejection("failure", Some(exception)))
case Success(
CreateDLCOffer(announcement,
contractInfo,
CreateDLCOffer(contractInfo,
collateral,
feeRateOpt,
locktime,
refundLT)) =>
complete {
if (!announcement.validateSignature) {
val announcements = contractInfo.oracleInfo match {
case OracleInfoV0TLV(announcement) => Vector(announcement)
case OracleInfoV1TLV(announcements) => announcements
case OracleInfoV2TLV(announcements, _) => announcements
}
if (!announcements.forall(_.validateSignature)) {
throw new RuntimeException(
s"Received Oracle announcement with invalid signature! ${announcement.hex}")
s"Received Oracle announcement with invalid signature! ${announcements
.map(_.hex)}")
}
val oracleInfo = OracleInfo.fromOracleAnnouncement(announcement)
wallet
.createDLCOffer(oracleInfo,
contractInfo,
.createDLCOffer(contractInfo,
collateral,
feeRateOpt,
locktime,

View file

@ -37,7 +37,7 @@ class DLCMessageTest extends BitcoinSAsyncTest {
it must "not allow a negative collateral for a DLCOffer" in {
assertThrows[IllegalArgumentException](
DLCOffer(
OracleAndContractInfo(OracleInfo.dummy, ContractInfo.empty),
ContractInfo.dummy,
DLCPublicKeys(dummyPubKey, dummyAddress),
Satoshis(-1),
Vector.empty,

View file

@ -21,7 +21,6 @@ class DLCStatusTest extends BitcoinSAsyncTest {
DLCStatus.Offered(offer.paramHash,
isInit,
offer.tempContractId,
offer.oracleInfo,
offer.contractInfo,
offer.timeouts,
offer.feeRate,
@ -48,7 +47,6 @@ class DLCStatusTest extends BitcoinSAsyncTest {
isInit,
offer.tempContractId,
contractId,
offer.oracleInfo,
offer.contractInfo,
offer.timeouts,
offer.feeRate,
@ -76,7 +74,6 @@ class DLCStatusTest extends BitcoinSAsyncTest {
isInit,
offer.tempContractId,
contractId,
offer.oracleInfo,
offer.contractInfo,
offer.timeouts,
offer.feeRate,
@ -105,7 +102,6 @@ class DLCStatusTest extends BitcoinSAsyncTest {
isInit,
offer.tempContractId,
contractId,
offer.oracleInfo,
offer.contractInfo,
offer.timeouts,
offer.feeRate,
@ -135,7 +131,6 @@ class DLCStatusTest extends BitcoinSAsyncTest {
isInit,
offer.tempContractId,
contractId,
offer.oracleInfo,
offer.contractInfo,
offer.timeouts,
offer.feeRate,
@ -173,7 +168,6 @@ class DLCStatusTest extends BitcoinSAsyncTest {
isInit,
offer.tempContractId,
contractId,
offer.oracleInfo,
offer.contractInfo,
offer.timeouts,
offer.feeRate,
@ -214,7 +208,6 @@ class DLCStatusTest extends BitcoinSAsyncTest {
isInit,
offer.tempContractId,
contractId,
offer.oracleInfo,
offer.contractInfo,
offer.timeouts,
offer.feeRate,
@ -250,7 +243,6 @@ class DLCStatusTest extends BitcoinSAsyncTest {
isInit,
offer.tempContractId,
contractId,
offer.oracleInfo,
offer.contractInfo,
offer.timeouts,
offer.feeRate,

View file

@ -19,19 +19,38 @@ sealed trait DLCMessage
object DLCMessage {
def calcParamHash(
oracleInfo: OracleInfo,
contractInfo: ContractInfo,
timeouts: DLCTimeouts): Sha256DigestBE = {
CryptoUtil
.sha256(oracleInfo.bytes ++ contractInfo.bytes ++ timeouts.bytes)
.sha256(contractInfo.bytes ++ timeouts.bytes)
.flip
}
sealed trait OracleInfo extends TLVSerializable[OracleInfoTLV] {
def pubKey: SchnorrPublicKey
sealed trait OracleInfo extends TLVSerializable[OracleInfoTLV]
sealed trait EnumOracleInfo extends OracleInfo
sealed trait NumericOracleInfo extends OracleInfo
object OracleInfo
extends TLVDeserializable[OracleInfoTLV, OracleInfo](OracleInfoTLV) {
override def fromTLV(tlv: OracleInfoTLV): OracleInfo = {
tlv match {
case tlv: OracleInfoV0TLV => SingleOracleInfo.fromTLV(tlv)
case tlv: OracleInfoV1TLV => ExactMultiOracleInfo.fromTLV(tlv)
case tlv: OracleInfoV2TLV => NumericMultiOracleInfo.fromTLV(tlv)
}
}
}
sealed trait SingleOracleInfo
extends OracleInfo
with TLVSerializable[OracleInfoV0TLV] {
def announcement: OracleAnnouncementTLV
def publicKey: SchnorrPublicKey = announcement.publicKey
/** The oracle's pre-committed nonces, in the correct order */
def nonces: Vector[SchnorrNonce]
def nonces: Vector[SchnorrNonce] = announcement.eventTLV.nonces
/** The order of the given sigs should correspond to the given outcome. */
def verifySigs(
@ -42,16 +61,42 @@ object DLCMessage {
* This point is used for adaptor signing.
*/
def sigPoint(outcome: DLCOutcomeType): ECPublicKey = {
pubKey.computeSigPoint(outcome.serialized, nonces)
publicKey.computeSigPoint(outcome.serialized, nonces)
}
override def toTLV: OracleInfoV0TLV = OracleInfoV0TLV(announcement)
}
object SingleOracleInfo
extends TLVDeserializable[OracleInfoV0TLV, SingleOracleInfo](
OracleInfoV0TLV) {
def apply(announcement: OracleAnnouncementTLV): SingleOracleInfo = {
announcement.eventTLV.eventDescriptor match {
case _: EnumEventDescriptorV0TLV =>
EnumSingleOracleInfo(announcement)
case _: NumericEventDescriptorTLV =>
NumericSingleOracleInfo(announcement)
}
}
def apply(tlv: OracleInfoV0TLV): SingleOracleInfo = {
SingleOracleInfo(tlv.announcement)
}
override def fromTLV(tlv: OracleInfoV0TLV): SingleOracleInfo = {
SingleOracleInfo(tlv)
}
}
case class SingleNonceOracleInfo(
pubKey: SchnorrPublicKey,
rValue: SchnorrNonce)
extends OracleInfo
with TLVSerializable[OracleInfoV0TLV] {
override def nonces: Vector[SchnorrNonce] = Vector(rValue)
case class EnumSingleOracleInfo(announcement: OracleAnnouncementTLV)
extends SingleOracleInfo
with EnumOracleInfo {
require(announcement.eventTLV.eventDescriptor
.isInstanceOf[EnumEventDescriptorV0TLV],
s"Enum OracleInfo requires EnumEventDescriptor, $announcement")
val nonce: SchnorrNonce = announcement.eventTLV.nonces.head
override def verifySigs(
outcome: DLCOutcomeType,
@ -61,39 +106,45 @@ object DLCMessage {
if (sigs.length != 1) {
throw new IllegalArgumentException(
s"Expected one signature, got $sigs")
} else if (sigs.head.rx != rValue) {
} else if (sigs.head.rx != nonce) {
throw new IllegalArgumentException(
s"Expected R value of $rValue, got ${sigs.head}")
s"Expected R value of $nonce, got ${sigs.head}")
} else {
pubKey.verify(CryptoUtil
.sha256DLCAttestation(outcome)
.bytes,
sigs.head)
publicKey.verify(CryptoUtil.sha256DLCAttestation(outcome).bytes,
sigs.head)
}
case UnsignedNumericOutcome(_) =>
throw new IllegalArgumentException(
s"Expected EnumOutcome, got $outcome")
}
}
override def toTLV: OracleInfoV0TLV = OracleInfoV0TLV(pubKey, rValue)
}
object SingleNonceOracleInfo
extends TLVDeserializable[OracleInfoV0TLV, SingleNonceOracleInfo](
object EnumSingleOracleInfo
extends TLVDeserializable[OracleInfoV0TLV, EnumSingleOracleInfo](
OracleInfoV0TLV) {
override def fromTLV(tlv: OracleInfoV0TLV): SingleNonceOracleInfo = {
SingleNonceOracleInfo(tlv.pubKey, tlv.rValue)
def dummyForKeys(
privKey: ECPrivateKey,
nonce: SchnorrNonce,
events: Vector[EnumOutcome]): EnumSingleOracleInfo = {
EnumSingleOracleInfo(
OracleAnnouncementV0TLV
.dummyForEventsAndKeys(privKey, nonce, events))
}
override def fromTLV(tlv: OracleInfoV0TLV): EnumSingleOracleInfo = {
EnumSingleOracleInfo(tlv.announcement)
}
}
case class MultiNonceOracleInfo(
pubKey: SchnorrPublicKey,
nonces: Vector[SchnorrNonce])
extends OracleInfo
with TLVSerializable[OracleInfoV1TLV] {
require(nonces.nonEmpty, "Must contain positive number of nonces.")
case class NumericSingleOracleInfo(announcement: OracleAnnouncementTLV)
extends SingleOracleInfo
with NumericOracleInfo {
require(
announcement.eventTLV.eventDescriptor
.isInstanceOf[NumericEventDescriptorTLV],
s"Numeric OracleInfo requires NumericEventDescriptor, $announcement")
override def verifySigs(
outcome: DLCOutcomeType,
@ -116,55 +167,117 @@ object DLCMessage {
sig.rx == nonce,
s"Unexpected nonce in ${sig.hex}, expected ${nonce.hex}")
result && pubKey.verify(CryptoUtil
.sha256DLCAttestation(digit.toString)
.bytes,
sig)
result && publicKey.verify(
CryptoUtil.sha256DLCAttestation(digit.toString).bytes,
sig)
}
}
}
override def toTLV: OracleInfoV1TLV = OracleInfoV1TLV(pubKey, nonces)
}
object MultiNonceOracleInfo
extends TLVDeserializable[OracleInfoV1TLV, MultiNonceOracleInfo](
OracleInfoV1TLV) {
object NumericSingleOracleInfo {
override def fromTLV(tlv: OracleInfoV1TLV): MultiNonceOracleInfo = {
MultiNonceOracleInfo(tlv.pubKey, tlv.nonces)
def dummyForKeys(
privKey: ECPrivateKey,
nonces: Vector[SchnorrNonce]): NumericSingleOracleInfo = {
NumericSingleOracleInfo(
OracleAnnouncementV0TLV.dummyForKeys(privKey, nonces))
}
}
object OracleInfo
extends TLVDeserializable[OracleInfoTLV, OracleInfo](OracleInfoTLV) {
sealed trait MultiOracleInfo[+T <: SingleOracleInfo]
extends OracleInfo
with TLVSerializable[MultiOracleInfoTLV] {
def announcements: Vector[OracleAnnouncementTLV]
val dummy: OracleInfo = SingleNonceOracleInfo(
ECPublicKey.freshPublicKey.schnorrPublicKey,
ECPublicKey.freshPublicKey.schnorrNonce)
// Override this with a val to invoke requirements
def singleOracleInfos: Vector[T]
}
def fromOracleAnnouncement(
announcement: OracleAnnouncementTLV): OracleInfo = {
announcement.eventTLV.eventDescriptor match {
case _: EnumEventDescriptorV0TLV | _: RangeEventDescriptorV0TLV =>
require(announcement.eventTLV.nonces.size == 1)
SingleNonceOracleInfo(announcement.publicKey,
announcement.eventTLV.nonces.head)
case _: DigitDecompositionEventDescriptorV0TLV =>
MultiNonceOracleInfo(announcement.publicKey,
announcement.eventTLV.nonces)
sealed trait ExactMultiOracleInfo[+T <: SingleOracleInfo]
extends MultiOracleInfo[T]
with TLVSerializable[OracleInfoV1TLV] {
override def toTLV: OracleInfoV1TLV = OracleInfoV1TLV(announcements)
}
object ExactMultiOracleInfo
extends TLVDeserializable[
OracleInfoV1TLV,
ExactMultiOracleInfo[SingleOracleInfo]](OracleInfoV1TLV) {
def apply(tlv: OracleInfoV1TLV): ExactMultiOracleInfo[SingleOracleInfo] = {
tlv.oracles.head.eventTLV.eventDescriptor match {
case _: EnumEventDescriptorV0TLV => EnumMultiOracleInfo(tlv.oracles)
case _: NumericEventDescriptorTLV =>
NumericExactMultiOracleInfo(tlv.oracles)
}
}
override def fromTLV(tlv: OracleInfoTLV): OracleInfo = {
tlv match {
case tlv: OracleInfoV0TLV => SingleNonceOracleInfo.fromTLV(tlv)
case tlv: OracleInfoV1TLV => MultiNonceOracleInfo.fromTLV(tlv)
}
override def fromTLV(
tlv: OracleInfoV1TLV): ExactMultiOracleInfo[SingleOracleInfo] = {
ExactMultiOracleInfo(tlv)
}
}
sealed trait ContractInfo extends TLVSerializable[ContractInfoTLV] {
case class EnumMultiOracleInfo(announcements: Vector[OracleAnnouncementTLV])
extends ExactMultiOracleInfo[EnumSingleOracleInfo]
with EnumOracleInfo {
override val singleOracleInfos: Vector[EnumSingleOracleInfo] =
announcements.map(EnumSingleOracleInfo.apply)
}
case class NumericExactMultiOracleInfo(
announcements: Vector[OracleAnnouncementTLV])
extends ExactMultiOracleInfo[NumericSingleOracleInfo]
with NumericOracleInfo {
val singleOracleInfos: Vector[NumericSingleOracleInfo] =
announcements.map(NumericSingleOracleInfo.apply)
}
case class NumericMultiOracleInfo(
announcements: Vector[OracleAnnouncementTLV],
maxErrorExp: Int,
minFailExp: Int,
maximizeCoverage: Boolean)
extends MultiOracleInfo[NumericSingleOracleInfo]
with TLVSerializable[OracleInfoV2TLV]
with NumericOracleInfo {
override val singleOracleInfos: Vector[NumericSingleOracleInfo] =
announcements.map(NumericSingleOracleInfo.apply)
override def toTLV: OracleInfoV2TLV = {
OracleInfoV2TLV(
announcements,
OracleParamsV0TLV(maxErrorExp, minFailExp, maximizeCoverage))
}
}
object NumericMultiOracleInfo
extends TLVDeserializable[OracleInfoV2TLV, NumericMultiOracleInfo](
OracleInfoV2TLV) {
def apply(
announcements: Vector[OracleAnnouncementTLV],
params: OracleParamsTLV): NumericMultiOracleInfo = {
params match {
case OracleParamsV0TLV(maxErrorExp, minFailExp, maximizeCoverage) =>
NumericMultiOracleInfo(announcements,
maxErrorExp,
minFailExp,
maximizeCoverage)
}
}
override def fromTLV(tlv: OracleInfoV2TLV): NumericMultiOracleInfo = {
NumericMultiOracleInfo(tlv.oracles, tlv.params)
}
}
sealed trait ContractDescriptor
extends TLVSerializable[ContractDescriptorTLV] {
/** Returns the counter-party's ContractInfo corresponding to this one.
*
@ -176,32 +289,30 @@ object DLCMessage {
* could lead to an off-by-one after rounding so that the sum above gives TC-1.
* In this example, only the offerer's ContractInfo should be used.
*/
def flip(totalCollateral: Satoshis): ContractInfo
def allOutcomes: Vector[DLCOutcomeType]
def apply(outcome: DLCOutcomeType): Satoshis
/** Returns the maximum payout this party could win from this contract */
def max: Satoshis
def flip(totalCollateral: Satoshis): ContractDescriptor
}
case class SingleNonceContractInfo(
outcomeValueMap: Vector[(EnumOutcome, Satoshis)])
extends ContractInfo
with TLVSerializable[ContractInfoV0TLV]
with SeqWrapper[(EnumOutcome, Satoshis)] {
object ContractDescriptor
extends TLVDeserializable[ContractDescriptorTLV, ContractDescriptor](
ContractDescriptorTLV) {
override def apply(outcome: DLCOutcomeType): Satoshis = {
outcome match {
case outcome: EnumOutcome =>
outcomeValueMap
.find(_._1 == outcome)
.map(_._2)
.getOrElse(throw new IllegalArgumentException(
s"No value found for key $outcome"))
case UnsignedNumericOutcome(_) =>
throw new IllegalArgumentException(s"Expected EnumOutcome: $outcome")
val empty: ContractDescriptor = EnumContractDescriptor(
Vector(EnumOutcome("") -> Satoshis.zero))
override def fromTLV(tlv: ContractDescriptorTLV): ContractDescriptor = {
tlv match {
case tlv: ContractDescriptorV0TLV => EnumContractDescriptor.fromTLV(tlv)
case tlv: ContractDescriptorV1TLV =>
NumericContractDescriptor.fromTLV(tlv)
}
}
}
case class EnumContractDescriptor(
outcomeValueMap: Vector[(EnumOutcome, Satoshis)])
extends ContractDescriptor
with TLVSerializable[ContractDescriptorV0TLV]
with SeqWrapper[(EnumOutcome, Satoshis)] {
override def wrapped: Vector[(EnumOutcome, Satoshis)] = outcomeValueMap
@ -209,34 +320,32 @@ object DLCMessage {
def values: Vector[Satoshis] = outcomeValueMap.map(_._2)
override def allOutcomes: Vector[DLCOutcomeType] = keys
override def max: Satoshis = values.maxBy(_.toLong)
override def toTLV: ContractInfoV0TLV =
ContractInfoV0TLV(outcomeValueMap.map {
override def toTLV: ContractDescriptorV0TLV =
ContractDescriptorV0TLV(outcomeValueMap.map {
case (outcome, amt) => outcome.outcome -> amt
})
override def flip(totalCollateral: Satoshis): SingleNonceContractInfo = {
SingleNonceContractInfo(outcomeValueMap.map {
override def flip(totalCollateral: Satoshis): EnumContractDescriptor = {
EnumContractDescriptor(outcomeValueMap.map {
case (hash, amt) => (hash, (totalCollateral - amt).satoshis)
})
}
}
object SingleNonceContractInfo
extends TLVDeserializable[ContractInfoV0TLV, SingleNonceContractInfo](
ContractInfoV0TLV) {
object EnumContractDescriptor
extends TLVDeserializable[
ContractDescriptorV0TLV,
EnumContractDescriptor](ContractDescriptorV0TLV) {
def fromStringVec(
vec: Vector[(String, Satoshis)]): SingleNonceContractInfo = {
SingleNonceContractInfo(vec.map {
vec: Vector[(String, Satoshis)]): EnumContractDescriptor = {
EnumContractDescriptor(vec.map {
case (str, amt) => EnumOutcome(str) -> amt
})
}
override def fromTLV(tlv: ContractInfoV0TLV): SingleNonceContractInfo = {
override def fromTLV(
tlv: ContractDescriptorV0TLV): EnumContractDescriptor = {
fromStringVec(tlv.outcomes)
}
}
@ -244,145 +353,124 @@ object DLCMessage {
/** Contains a deterministically compressed set of outcomes computed from
* a given payout curve.
*/
case class MultiNonceContractInfo(
case class NumericContractDescriptor(
outcomeValueFunc: DLCPayoutCurve,
base: Int,
numDigits: Int,
totalCollateral: Satoshis,
roundingIntervals: RoundingIntervals)
extends ContractInfo
with TLVSerializable[ContractInfoV1TLV] {
extends ContractDescriptor
with TLVSerializable[ContractDescriptorV1TLV] {
/** Vector is always the most significant digits */
lazy val outcomeVec: Vector[(Vector[Int], Satoshis)] =
CETCalculator.computeCETs(base,
numDigits,
outcomeValueFunc,
totalCollateral,
roundingIntervals)
override def apply(outcome: DLCOutcomeType): Satoshis = {
outcome match {
case UnsignedNumericOutcome(digits) =>
CETCalculator.searchForPrefix(digits, outcomeVec)(_._1) match {
case Some((_, amt)) => amt
case None =>
throw new IllegalArgumentException(
s"Unrecognized outcome: $digits")
}
case EnumOutcome(_) =>
throw new IllegalArgumentException(
s"Expected UnsignedNumericOutcome: $outcome")
}
}
override lazy val allOutcomes: Vector[DLCOutcomeType] =
outcomeVec.map { case (outcome, _) => UnsignedNumericOutcome(outcome) }
override val max: Satoshis = totalCollateral
override def flip(totalCollateral: Satoshis): MultiNonceContractInfo = {
require(
totalCollateral == this.totalCollateral,
s"Input total collateral ($totalCollateral) did not match ${this.totalCollateral}")
override def flip(totalCollateral: Satoshis): NumericContractDescriptor = {
val flippedFunc = DLCPayoutCurve(outcomeValueFunc.points.map { point =>
point.copy(payout = totalCollateral.toLong - point.payout)
})
MultiNonceContractInfo(
NumericContractDescriptor(
flippedFunc,
base,
numDigits,
totalCollateral,
roundingIntervals
)
}
override lazy val toTLV: ContractInfoV1TLV = {
ContractInfoV1TLV(base,
numDigits,
totalCollateral,
outcomeValueFunc.toTLV,
roundingIntervals.toTLV)
override def toTLV: ContractDescriptorV1TLV = {
ContractDescriptorV1TLV(numDigits,
outcomeValueFunc.toTLV,
roundingIntervals.toTLV)
}
}
object MultiNonceContractInfo
extends TLVDeserializable[ContractInfoV1TLV, MultiNonceContractInfo](
ContractInfoV1TLV) {
object NumericContractDescriptor
extends TLVDeserializable[
ContractDescriptorV1TLV,
NumericContractDescriptor](ContractDescriptorV1TLV) {
override def fromTLV(tlv: ContractInfoV1TLV): MultiNonceContractInfo = {
MultiNonceContractInfo(DLCPayoutCurve.fromTLV(tlv.payoutFunction),
tlv.base,
tlv.numDigits,
tlv.totalCollateral,
RoundingIntervals.fromTLV(tlv.roundingIntervals))
override def fromTLV(
tlv: ContractDescriptorV1TLV): NumericContractDescriptor = {
NumericContractDescriptor(
DLCPayoutCurve.fromTLV(tlv.payoutFunction),
tlv.numDigits,
RoundingIntervals.fromTLV(tlv.roundingIntervals)
)
}
}
object ContractInfo
extends TLVDeserializable[ContractInfoTLV, ContractInfo](
ContractInfoTLV) {
case class ContractDescriptorWithCollateral(
totalCollateral: Satoshis,
contractDescriptor: ContractDescriptor) {
val empty: ContractInfo = SingleNonceContractInfo(
Vector(EnumOutcome("") -> Satoshis.zero))
/** Vector is always the most significant digits */
lazy val outcomeVecOpt: Option[Vector[(Vector[Int], Satoshis)]] = {
contractDescriptor match {
case _: EnumContractDescriptor => None
case descriptor: NumericContractDescriptor =>
val outcomeVec = CETCalculator.computeCETs(
base = 2,
descriptor.numDigits,
descriptor.outcomeValueFunc,
totalCollateral,
descriptor.roundingIntervals)
override def fromTLV(tlv: ContractInfoTLV): ContractInfo = {
tlv match {
case tlv: ContractInfoV0TLV => SingleNonceContractInfo.fromTLV(tlv)
case tlv: ContractInfoV1TLV => MultiNonceContractInfo.fromTLV(tlv)
Some(outcomeVec)
}
}
}
case class OracleAndContractInfo(
oracleInfo: OracleInfo,
offerContractInfo: ContractInfo,
totalCollateral: Satoshis) {
(oracleInfo, offerContractInfo) match {
case (_: SingleNonceOracleInfo, info: SingleNonceContractInfo) =>
require(
info.max <= totalCollateral,
s"Cannot have payout larger than totalCollateral ($totalCollateral): $info")
case (_: MultiNonceOracleInfo, info: MultiNonceContractInfo) =>
require(
info.totalCollateral == totalCollateral,
s"Expected total collateral of $totalCollateral, got ${info.totalCollateral}")
case (_: OracleInfo, _: ContractInfo) =>
throw new IllegalArgumentException(
s"All infos must be for the same kind of outcome: $this")
}
def verifySigs(
outcome: DLCOutcomeType,
sigs: Vector[SchnorrDigitalSignature]): Boolean = {
oracleInfo.verifySigs(outcome, sigs)
}
def findOutcome(
sigs: Vector[SchnorrDigitalSignature]): Option[DLCOutcomeType] = {
offerContractInfo match {
case SingleNonceContractInfo(_) =>
allOutcomes.find(verifySigs(_, sigs))
case MultiNonceContractInfo(_, base, _, _, _) =>
val digitsSigned = sigs.map { sig =>
(0 until base)
.find { possibleDigit =>
oracleInfo.pubKey.verify(
CryptoUtil
.sha256DLCAttestation(possibleDigit.toString)
.bytes,
sig)
}
.getOrElse(throw new IllegalArgumentException(
s"Signature $sig does not match any digit 0-${base - 1}"))
lazy val allOutcomes: Vector[DLCOutcomeType] = {
contractDescriptor match {
case descriptor: EnumContractDescriptor => descriptor.keys
case _: NumericContractDescriptor =>
outcomeVecOpt.get.map {
case (outcome, _) => UnsignedNumericOutcome(outcome)
}
CETCalculator.searchForNumericOutcome(digitsSigned, allOutcomes)
}
}
/** Returns the maximum payout this party could win from this contract */
val max: Satoshis = contractDescriptor match {
case descriptor: EnumContractDescriptor =>
descriptor.values.maxBy(_.toLong)
case _: NumericContractDescriptor => totalCollateral
}
}
case class ContractInfo(
totalCollateral: Satoshis,
contractDescriptor: ContractDescriptor,
oracleInfo: OracleInfo)
extends TLVSerializable[ContractInfoV0TLV] {
override def toTLV: ContractInfoV0TLV = {
ContractInfoV0TLV(totalCollateral,
contractDescriptor.toTLV,
oracleInfo.toTLV)
}
val descriptorWithCollateral: ContractDescriptorWithCollateral =
ContractDescriptorWithCollateral(totalCollateral, contractDescriptor)
def outcomeVecOpt: Option[Vector[(Vector[Int], Satoshis)]] =
descriptorWithCollateral.outcomeVecOpt
def allOutcomes: Vector[DLCOutcomeType] =
descriptorWithCollateral.allOutcomes
def max: Satoshis = descriptorWithCollateral.max
val descriptorAndInfo: Either[
(EnumContractDescriptor, EnumOracleInfo),
(NumericContractDescriptor, NumericOracleInfo)] =
(contractDescriptor, oracleInfo) match {
case (contractDescriptor: EnumContractDescriptor,
oracleInfo: EnumOracleInfo) =>
Left((contractDescriptor, oracleInfo))
case (contractDescriptor: NumericContractDescriptor,
oracleInfo: NumericOracleInfo) =>
Right((contractDescriptor, oracleInfo))
case (_: ContractDescriptor, _: OracleInfo) =>
throw new IllegalArgumentException(
s"All infos must be for the same kind of outcome: $this")
}
lazy val outcomeMap: Map[
DLCOutcomeType,
(ECPublicKey, Satoshis, Satoshis)] = {
@ -390,15 +478,83 @@ object DLCMessage {
HashMap.newBuilder[DLCOutcomeType, (ECPublicKey, Satoshis, Satoshis)]
allOutcomes.foreach { msg =>
val offerPayout = offerContractInfo(msg)
val offerPayout = apply(msg)
val acceptPayout = (totalCollateral - offerPayout).satoshis
val adaptorPoint =
oracleInfo.asInstanceOf[SingleOracleInfo].sigPoint(msg) // FIXME
builder.+=(msg -> (oracleInfo.sigPoint(msg), offerPayout, acceptPayout))
builder.+=(msg -> (adaptorPoint, offerPayout, acceptPayout))
}
builder.result()
}
def apply(outcome: DLCOutcomeType): Satoshis = {
descriptorAndInfo match {
case Left((EnumContractDescriptor(outcomeValueMap), _)) =>
outcome match {
case outcome: EnumOutcome =>
outcomeValueMap
.find(_._1 == outcome)
.map(_._2)
.getOrElse(throw new IllegalArgumentException(
s"No value found for key $outcome"))
case UnsignedNumericOutcome(_) =>
throw new IllegalArgumentException(
s"Expected EnumOutcome: $outcome")
}
case Right((_, _)) =>
outcome match {
case UnsignedNumericOutcome(digits) =>
CETCalculator.searchForPrefix(digits, outcomeVecOpt.get)(
_._1) match {
case Some((_, amt)) => amt
case None =>
throw new IllegalArgumentException(
s"Unrecognized outcome: $digits")
}
case EnumOutcome(_) =>
throw new IllegalArgumentException(
s"Expected UnsignedNumericOutcome: $outcome")
}
}
}
def verifySigs(
outcome: DLCOutcomeType,
sigs: Vector[SchnorrDigitalSignature]): Boolean = {
oracleInfo
.asInstanceOf[SingleOracleInfo]
.verifySigs(outcome, sigs) // FIXME
}
def findOutcome(
sigs: Vector[SchnorrDigitalSignature]): Option[DLCOutcomeType] = {
contractDescriptor match {
case _: EnumContractDescriptor =>
allOutcomes.find(verifySigs(_, sigs))
case _: NumericContractDescriptor =>
val base = 2
val digitsSigned = sigs.map { sig =>
(0 until base)
.find { possibleDigit =>
// FIXME
oracleInfo
.asInstanceOf[SingleOracleInfo]
.publicKey
.verify(CryptoUtil
.sha256DLCAttestation(possibleDigit.toString)
.bytes,
sig)
}
.getOrElse(throw new IllegalArgumentException(
s"Signature $sig does not match any digit 0-${base - 1}"))
}
CETCalculator.searchForNumericOutcome(digitsSigned, allOutcomes)
}
}
def resultOfOutcome(
outcome: DLCOutcomeType): (ECPublicKey, Satoshis, Satoshis) = {
outcomeMap(outcome)
@ -408,9 +564,6 @@ object DLCMessage {
resultOfOutcome(outcome)._1
}
lazy val allOutcomes: Vector[DLCOutcomeType] =
offerContractInfo.allOutcomes
/** Returns the payouts for the signature as (toOffer, toAccept) */
def getPayouts(
sigs: Vector[SchnorrDigitalSignature]): (Satoshis, Satoshis) = {
@ -432,40 +585,51 @@ object DLCMessage {
def updateOnAccept(
newTotalCollateral: Satoshis,
negotiationFields: DLCAccept.NegotiationFields): OracleAndContractInfo = {
negotiationFields: DLCAccept.NegotiationFields): ContractInfo = {
if (newTotalCollateral == totalCollateral) {
this
} else {
offerContractInfo match {
case _: SingleNonceContractInfo =>
contractDescriptor match {
case _: EnumContractDescriptor =>
if (negotiationFields != DLCAccept.NoNegotiationFields) {
throw new IllegalArgumentException(
s"Cannot have rounding intervals for single nonce contract: $negotiationFields")
}
this.copy(totalCollateral = newTotalCollateral)
case info: MultiNonceContractInfo =>
case descriptor: NumericContractDescriptor =>
val newRoundingIntervals = negotiationFields match {
case DLCAccept.NegotiationFieldsV1(acceptRoundingIntervals) =>
info.roundingIntervals.minRoundingWith(acceptRoundingIntervals)
case DLCAccept.NoNegotiationFields => info.roundingIntervals
descriptor.roundingIntervals.minRoundingWith(
acceptRoundingIntervals)
case DLCAccept.NoNegotiationFields => descriptor.roundingIntervals
}
this.copy(offerContractInfo =
info.copy(totalCollateral = newTotalCollateral,
roundingIntervals = newRoundingIntervals),
totalCollateral = newTotalCollateral)
this.copy(
totalCollateral = newTotalCollateral,
contractDescriptor =
descriptor.copy(roundingIntervals = newRoundingIntervals))
}
}
}
}
object OracleAndContractInfo {
object ContractInfo
extends TLVDeserializable[ContractInfoV0TLV, ContractInfo](
ContractInfoV0TLV) {
lazy val dummy: ContractInfo = fromTLV(ContractInfoV0TLV.dummy)
override def fromTLV(tlv: ContractInfoV0TLV): ContractInfo = {
ContractInfo(tlv.totalCollateral,
ContractDescriptor.fromTLV(tlv.contractDescriptor),
OracleInfo.fromTLV(tlv.oracleInfo))
}
def apply(
oracleInfo: OracleInfo,
offerContractInfo: ContractInfo): OracleAndContractInfo = {
OracleAndContractInfo(oracleInfo,
offerContractInfo,
offerContractInfo.max)
enumDescriptor: EnumContractDescriptor,
enumOracleInfo: EnumOracleInfo): ContractInfo = {
ContractInfo(totalCollateral = enumDescriptor.values.maxBy(_.toLong),
enumDescriptor,
enumOracleInfo)
}
}
@ -486,7 +650,7 @@ object DLCMessage {
/**
* The initiating party starts the protocol by sending an offer message to the other party.
*
* @param oracleAndContractInfo The oracle public key and R point(s) to use to build the CETs as
* @param contractInfo The oracle public key and R point(s) to use to build the CETs as
* well as meta information to identify the oracle to be used in the contract,
* and a map to be used to create CETs.
* @param pubKeys The relevant public keys that the initiator will be using
@ -497,7 +661,7 @@ object DLCMessage {
* @param timeouts The set of timeouts for the CETs
*/
case class DLCOffer(
oracleAndContractInfo: OracleAndContractInfo,
contractInfo: ContractInfo,
pubKeys: DLCPublicKeys,
totalCollateral: Satoshis,
fundingInputs: Vector[DLCFundingInput],
@ -506,11 +670,10 @@ object DLCMessage {
timeouts: DLCTimeouts)
extends DLCSetupMessage {
val oracleInfo: OracleInfo = oracleAndContractInfo.oracleInfo
val contractInfo: ContractInfo = oracleAndContractInfo.offerContractInfo
val oracleInfo: OracleInfo = contractInfo.oracleInfo
val contractDescriptor: ContractDescriptor = contractInfo.contractDescriptor
lazy val paramHash: Sha256DigestBE =
calcParamHash(oracleInfo, contractInfo, timeouts)
lazy val paramHash: Sha256DigestBE = calcParamHash(contractInfo, timeouts)
val tempContractId: Sha256Digest =
CryptoUtil.sha256(toMessage.bytes)
@ -523,7 +686,6 @@ object DLCMessage {
contractFlags = 0x00,
chainHash = chainHash,
contractInfo.toTLV,
oracleInfo.toTLV,
fundingPubKey = pubKeys.fundingKey,
payoutSPK = pubKeys.payoutAddress.scriptPubKey,
totalCollateralSatoshis = totalCollateral,
@ -546,10 +708,9 @@ object DLCMessage {
val network = Networks.fromChainHash(offer.chainHash.flip)
val contractInfo = ContractInfo.fromTLV(offer.contractInfo)
val oracleInfo = OracleInfo.fromTLV(offer.oracleInfo)
DLCOffer(
oracleAndContractInfo = OracleAndContractInfo(oracleInfo, contractInfo),
contractInfo = contractInfo,
pubKeys = DLCPublicKeys(
offer.fundingPubKey,
BitcoinAddress.fromScriptPubKey(offer.payoutSPK, network)),
@ -693,12 +854,7 @@ object DLCMessage {
accept: DLCAcceptTLV,
network: NetworkParameters,
contractInfo: ContractInfo): DLCAccept = {
contractInfo match {
case info: SingleNonceContractInfo =>
fromTLV(accept, network, info.keys)
case multi: MultiNonceContractInfo =>
fromTLV(accept, network, multi.allOutcomes)
}
fromTLV(accept, network, contractInfo.allOutcomes)
}
def fromTLV(accept: DLCAcceptTLV, offer: DLCOffer): DLCAccept = {
@ -764,18 +920,10 @@ object DLCMessage {
}
def fromTLV(sign: DLCSignTLV, offer: DLCOffer): DLCSign = {
offer.contractInfo match {
case info: SingleNonceContractInfo =>
fromTLV(sign,
offer.pubKeys.fundingKey,
info.keys,
offer.fundingInputs.map(_.outPoint))
case multi: MultiNonceContractInfo =>
fromTLV(sign,
offer.pubKeys.fundingKey,
multi.allOutcomes,
offer.fundingInputs.map(_.outPoint))
}
fromTLV(sign,
offer.pubKeys.fundingKey,
offer.contractInfo.allOutcomes,
offer.fundingInputs.map(_.outPoint))
}
def fromMessage(sign: LnMessage[DLCSignTLV], offer: DLCOffer): DLCSign = {

View file

@ -25,8 +25,8 @@ sealed trait DLCStatus {
def isInitiator: Boolean
def state: DLCState
def tempContractId: Sha256Digest
def oracleInfo: OracleInfo
def contractInfo: ContractInfo
def oracleInfo: OracleInfo = contractInfo.oracleInfo
def timeouts: DLCTimeouts
def feeRate: FeeUnit
def totalCollateral: CurrencyUnit
@ -59,7 +59,6 @@ object DLCStatus {
paramHash: Sha256DigestBE,
isInitiator: Boolean,
tempContractId: Sha256Digest,
oracleInfo: OracleInfo,
contractInfo: ContractInfo,
timeouts: DLCTimeouts,
feeRate: FeeUnit,
@ -74,7 +73,6 @@ object DLCStatus {
isInitiator: Boolean,
tempContractId: Sha256Digest,
contractId: ByteVector,
oracleInfo: OracleInfo,
contractInfo: ContractInfo,
timeouts: DLCTimeouts,
feeRate: FeeUnit,
@ -89,7 +87,6 @@ object DLCStatus {
isInitiator: Boolean,
tempContractId: Sha256Digest,
contractId: ByteVector,
oracleInfo: OracleInfo,
contractInfo: ContractInfo,
timeouts: DLCTimeouts,
feeRate: FeeUnit,
@ -104,7 +101,6 @@ object DLCStatus {
isInitiator: Boolean,
tempContractId: Sha256Digest,
contractId: ByteVector,
oracleInfo: OracleInfo,
contractInfo: ContractInfo,
timeouts: DLCTimeouts,
feeRate: FeeUnit,
@ -120,7 +116,6 @@ object DLCStatus {
isInitiator: Boolean,
tempContractId: Sha256Digest,
contractId: ByteVector,
oracleInfo: OracleInfo,
contractInfo: ContractInfo,
timeouts: DLCTimeouts,
feeRate: FeeUnit,
@ -136,7 +131,6 @@ object DLCStatus {
isInitiator: Boolean,
tempContractId: Sha256Digest,
contractId: ByteVector,
oracleInfo: OracleInfo,
contractInfo: ContractInfo,
timeouts: DLCTimeouts,
feeRate: FeeUnit,
@ -155,7 +149,6 @@ object DLCStatus {
isInitiator: Boolean,
tempContractId: Sha256Digest,
contractId: ByteVector,
oracleInfo: OracleInfo,
contractInfo: ContractInfo,
timeouts: DLCTimeouts,
feeRate: FeeUnit,
@ -175,7 +168,6 @@ object DLCStatus {
isInitiator: Boolean,
tempContractId: Sha256Digest,
contractId: ByteVector,
oracleInfo: OracleInfo,
contractInfo: ContractInfo,
timeouts: DLCTimeouts,
feeRate: FeeUnit,
@ -244,8 +236,9 @@ object DLCStatus {
require(cetSigs.size == 2,
s"There must be only 2 signatures, got ${cetSigs.size}")
val oraclePubKey = offer.oracleInfo.pubKey
val rVals = offer.oracleInfo.nonces
val oraclePubKey =
offer.oracleInfo.asInstanceOf[SingleOracleInfo].publicKey // FIXME
val rVals = offer.oracleInfo.asInstanceOf[SingleOracleInfo].nonces // FIXME
def aggregateR(numSigs: Int): SchnorrNonce = {
rVals.take(numSigs).map(_.publicKey).reduce(_.add(_)).schnorrNonce
@ -296,8 +289,8 @@ object DLCStatus {
val outcomeValues = wCET.outputs.map(_.value).sorted
val totalCollateral = offer.totalCollateral + accept.totalCollateral
val possibleMessages = offer.contractInfo match {
case DLCMessage.SingleNonceContractInfo(outcomeValueMap) =>
val possibleMessages = offer.contractInfo.contractDescriptor match {
case DLCMessage.EnumContractDescriptor(outcomeValueMap) =>
outcomeValueMap
.filter {
case (_, amt) =>
@ -306,8 +299,8 @@ object DLCStatus {
.sorted == outcomeValues
}
.map(_._1)
case info: DLCMessage.MultiNonceContractInfo =>
info.outcomeVec
case _: DLCMessage.NumericContractDescriptor =>
offer.contractInfo.outcomeVecOpt.get
.filter {
case (_, amt) =>
val amts = Vector(amt, totalCollateral - amt)
@ -350,7 +343,7 @@ object DLCStatus {
val sigOpt = outcomeSigs.find {
case (outcome, adaptorSig) =>
val possibleOracleSig = sigFromMsgAndSigs(outcome, adaptorSig, cetSig)
val sigPoint = offer.oracleAndContractInfo.sigPointForOutcome(outcome)
val sigPoint = offer.contractInfo.sigPointForOutcome(outcome)
possibleOracleSig.sig.getPublicKey == sigPoint
}

View file

@ -155,6 +155,8 @@ object TLV extends TLVParentFactory[TLV] {
OracleEventV0TLV,
RoundingIntervalsV0TLV,
PayoutFunctionV0TLV,
OracleParamsV0TLV,
ContractInfoV0TLV,
FundingInputV0TLV,
CETSignaturesV0TLV,
FundingSignaturesV0TLV,
@ -162,7 +164,7 @@ object TLV extends TLVParentFactory[TLV] {
DLCAcceptTLV,
DLCSignTLV
) ++ EventDescriptorTLV.allFactories ++
ContractInfoTLV.allFactories ++
ContractDescriptorTLV.allFactories ++
OracleInfoTLV.allFactories ++
OracleAnnouncementTLV.allFactories ++
NegotiationFieldsTLV.allFactories
@ -806,26 +808,58 @@ object OracleAnnouncementV0TLV extends TLVFactory[OracleAnnouncementV0TLV] {
UInt32.zero,
EnumEventDescriptorV0TLV.dummy,
"dummy")
val sig = priv.schnorrSign(CryptoUtil.sha256(event.bytes).bytes)
val sig =
priv.schnorrSign(CryptoUtil.sha256DLCAnnouncement(event.bytes).bytes)
OracleAnnouncementV0TLV(sig, priv.schnorrPublicKey, event)
}
def dummyForEventsAndKeys(
privKey: ECPrivateKey,
nonce: SchnorrNonce,
events: Vector[EnumOutcome]): OracleAnnouncementTLV = {
val event = OracleEventV0TLV(
Vector(nonce),
UInt32.zero,
EnumEventDescriptorV0TLV(events.map(outcome => outcome.outcome)),
"dummy")
val sig =
privKey.schnorrSign(CryptoUtil.sha256DLCAnnouncement(event.bytes).bytes)
OracleAnnouncementV0TLV(sig, privKey.schnorrPublicKey, event)
}
def dummyForKeys(
privKey: ECPrivateKey,
nonces: Vector[SchnorrNonce]): OracleAnnouncementTLV = {
val eventDescriptor = DigitDecompositionEventDescriptorV0TLV(UInt16(2),
isSigned =
false,
nonces.length,
"dummy",
Int32.zero)
val event = OracleEventV0TLV(nonces, UInt32.zero, eventDescriptor, "dummy")
val sig =
privKey.schnorrSign(CryptoUtil.sha256DLCAnnouncement(event.bytes).bytes)
OracleAnnouncementV0TLV(sig, privKey.schnorrPublicKey, event)
}
}
sealed trait ContractInfoTLV extends TLV
sealed trait ContractDescriptorTLV extends TLV
object ContractInfoTLV extends TLVParentFactory[ContractInfoTLV] {
object ContractDescriptorTLV extends TLVParentFactory[ContractDescriptorTLV] {
val allFactories: Vector[TLVFactory[ContractInfoTLV]] =
Vector(ContractInfoV0TLV, ContractInfoV1TLV)
val allFactories: Vector[TLVFactory[ContractDescriptorTLV]] =
Vector(ContractDescriptorV0TLV, ContractDescriptorV1TLV)
override def typeName: String = "ContractInfoTLV"
override def typeName: String = "ContractDescriptorTLV"
}
/** @see https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#version-0-contract_info */
case class ContractInfoV0TLV(outcomes: Vector[(String, Satoshis)])
extends ContractInfoTLV {
override val tpe: BigSizeUInt = ContractInfoV0TLV.tpe
case class ContractDescriptorV0TLV(outcomes: Vector[(String, Satoshis)])
extends ContractDescriptorTLV {
override val tpe: BigSizeUInt = ContractDescriptorV0TLV.tpe
override val value: ByteVector = {
bigSizePrefixedList[(String, Satoshis)](
@ -838,10 +872,10 @@ case class ContractInfoV0TLV(outcomes: Vector[(String, Satoshis)])
}
}
object ContractInfoV0TLV extends TLVFactory[ContractInfoV0TLV] {
object ContractDescriptorV0TLV extends TLVFactory[ContractDescriptorV0TLV] {
override val tpe: BigSizeUInt = BigSizeUInt(42768)
override def fromTLVValue(value: ByteVector): ContractInfoV0TLV = {
override def fromTLVValue(value: ByteVector): ContractDescriptorV0TLV = {
val iter = ValueIterator(value)
val outcomes = iter.takeBigSizePrefixedList { () =>
@ -851,7 +885,7 @@ object ContractInfoV0TLV extends TLVFactory[ContractInfoV0TLV] {
outcome -> amt
}
ContractInfoV0TLV(outcomes)
ContractDescriptorV0TLV(outcomes)
}
}
@ -951,41 +985,31 @@ object PayoutFunctionV0TLV extends TLVFactory[PayoutFunctionV0TLV] {
}
}
case class ContractInfoV1TLV(
base: Int,
case class ContractDescriptorV1TLV(
numDigits: Int,
totalCollateral: Satoshis,
payoutFunction: PayoutFunctionV0TLV,
roundingIntervals: RoundingIntervalsV0TLV)
extends ContractInfoTLV {
override val tpe: BigSizeUInt = ContractInfoV1TLV.tpe
extends ContractDescriptorTLV {
override val tpe: BigSizeUInt = ContractDescriptorV1TLV.tpe
override val value: ByteVector = {
BigSizeUInt(base).bytes ++
UInt16(numDigits).bytes ++
satBytes(totalCollateral) ++
UInt16(numDigits).bytes ++
payoutFunction.bytes ++
roundingIntervals.bytes
}
}
object ContractInfoV1TLV extends TLVFactory[ContractInfoV1TLV] {
object ContractDescriptorV1TLV extends TLVFactory[ContractDescriptorV1TLV] {
override val tpe: BigSizeUInt = BigSizeUInt(42784)
override def fromTLVValue(value: ByteVector): ContractInfoV1TLV = {
override def fromTLVValue(value: ByteVector): ContractDescriptorV1TLV = {
val iter = ValueIterator(value)
val base = iter.takeBigSize()
val numDigits = iter.takeU16()
val totalCollateral = iter.takeSats()
val payoutFunction = iter.take(PayoutFunctionV0TLV)
val roundingIntervals = iter.take(RoundingIntervalsV0TLV)
ContractInfoV1TLV(base.toInt,
numDigits.toInt,
totalCollateral,
payoutFunction,
roundingIntervals)
ContractDescriptorV1TLV(numDigits.toInt, payoutFunction, roundingIntervals)
}
}
@ -994,17 +1018,17 @@ sealed trait OracleInfoTLV extends TLV
object OracleInfoTLV extends TLVParentFactory[OracleInfoTLV] {
override val allFactories: Vector[TLVFactory[OracleInfoTLV]] =
Vector(OracleInfoV0TLV, OracleInfoV1TLV)
Vector(OracleInfoV0TLV, OracleInfoV1TLV, OracleInfoV2TLV)
override def typeName: String = "OracleInfoTLV"
}
case class OracleInfoV0TLV(pubKey: SchnorrPublicKey, rValue: SchnorrNonce)
case class OracleInfoV0TLV(announcement: OracleAnnouncementTLV)
extends OracleInfoTLV {
override val tpe: BigSizeUInt = OracleInfoV0TLV.tpe
override val value: ByteVector = {
pubKey.bytes ++ rValue.bytes
announcement.bytes
}
}
@ -1014,21 +1038,20 @@ object OracleInfoV0TLV extends TLVFactory[OracleInfoV0TLV] {
override def fromTLVValue(value: ByteVector): OracleInfoV0TLV = {
val iter = ValueIterator(value)
val pubKey = iter.take(SchnorrPublicKey, 32)
val rValue = iter.take(SchnorrNonce, 32)
val announcement = iter.take(OracleAnnouncementTLV)
OracleInfoV0TLV(pubKey, rValue)
OracleInfoV0TLV(announcement)
}
}
case class OracleInfoV1TLV(
pubKey: SchnorrPublicKey,
nonces: Vector[SchnorrNonce])
extends OracleInfoTLV {
sealed trait MultiOracleInfoTLV extends OracleInfoTLV
case class OracleInfoV1TLV(oracles: Vector[OracleAnnouncementTLV])
extends MultiOracleInfoTLV {
override val tpe: BigSizeUInt = OracleInfoV1TLV.tpe
override val value: ByteVector = {
pubKey.bytes ++ u16PrefixedList(nonces)
u16PrefixedList(oracles)
}
}
@ -1038,10 +1061,100 @@ object OracleInfoV1TLV extends TLVFactory[OracleInfoV1TLV] {
override def fromTLVValue(value: ByteVector): OracleInfoV1TLV = {
val iter = ValueIterator(value)
val pubKey = iter.take(SchnorrPublicKey, 32)
val nonces = iter.takeU16PrefixedList(() => iter.take(SchnorrNonce, 32))
val oracles =
iter.takeU16PrefixedList(() => iter.take(OracleAnnouncementTLV))
OracleInfoV1TLV(pubKey, nonces)
OracleInfoV1TLV(oracles)
}
}
sealed trait OracleParamsTLV extends TLV
case class OracleParamsV0TLV(
maxErrorExp: Int,
minFailExp: Int,
maximizeCoverage: Boolean)
extends OracleParamsTLV {
override val tpe: BigSizeUInt = OracleParamsV0TLV.tpe
override val value: ByteVector = {
UInt16(maxErrorExp).bytes ++
UInt16(minFailExp).bytes ++
boolBytes(maximizeCoverage)
}
}
object OracleParamsV0TLV extends TLVFactory[OracleParamsV0TLV] {
override val tpe: BigSizeUInt = BigSizeUInt(55338)
override def fromTLVValue(value: ByteVector): OracleParamsV0TLV = {
val iter = ValueIterator(value)
val maxErrorExp = iter.takeU16().toInt
val minFailExp = iter.takeU16().toInt
val maximizeCoverage = iter.takeBoolean()
OracleParamsV0TLV(maxErrorExp, minFailExp, maximizeCoverage)
}
}
case class OracleInfoV2TLV(
oracles: Vector[OracleAnnouncementTLV],
params: OracleParamsTLV)
extends MultiOracleInfoTLV {
override val tpe: BigSizeUInt = OracleInfoV2TLV.tpe
override val value: ByteVector = {
u16PrefixedList(oracles) ++ params.bytes
}
}
object OracleInfoV2TLV extends TLVFactory[OracleInfoV2TLV] {
override val tpe: BigSizeUInt = BigSizeUInt(55340)
override def fromTLVValue(value: ByteVector): OracleInfoV2TLV = {
val iter = ValueIterator(value)
val oracles =
iter.takeU16PrefixedList(() => iter.take(OracleAnnouncementTLV))
val params = iter.take(OracleParamsV0TLV)
OracleInfoV2TLV(oracles, params)
}
}
case class ContractInfoV0TLV(
totalCollateral: Satoshis,
contractDescriptor: ContractDescriptorTLV,
oracleInfo: OracleInfoTLV)
extends TLV {
override val tpe: BigSizeUInt = ContractInfoV0TLV.tpe
override val value: ByteVector = {
satBytes(totalCollateral) ++
contractDescriptor.bytes ++
oracleInfo.bytes
}
}
object ContractInfoV0TLV extends TLVFactory[ContractInfoV0TLV] {
override val tpe: BigSizeUInt = BigSizeUInt(55342)
val dummy: ContractInfoV0TLV = {
ContractInfoV0TLV(
Satoshis.zero,
ContractDescriptorV0TLV(Vector("dummy" -> Satoshis(10000))),
OracleInfoV0TLV(OracleAnnouncementV0TLV.dummy))
}
override def fromTLVValue(value: ByteVector): ContractInfoV0TLV = {
val iter = ValueIterator(value)
val totalCollateral = iter.takeSats()
val contractDescriptor = iter.take(ContractDescriptorTLV)
val oracleInfo = iter.take(OracleInfoTLV)
ContractInfoV0TLV(totalCollateral, contractDescriptor, oracleInfo)
}
}
@ -1174,8 +1287,7 @@ object FundingSignaturesV0TLV extends TLVFactory[FundingSignaturesV0TLV] {
case class DLCOfferTLV(
contractFlags: Byte,
chainHash: DoubleSha256Digest,
contractInfo: ContractInfoTLV,
oracleInfo: OracleInfoTLV,
contractInfo: ContractInfoV0TLV,
fundingPubKey: ECPublicKey,
payoutSPK: ScriptPubKey,
totalCollateralSatoshis: Satoshis,
@ -1191,7 +1303,6 @@ case class DLCOfferTLV(
ByteVector(contractFlags) ++
chainHash.bytes ++
contractInfo.bytes ++
oracleInfo.bytes ++
fundingPubKey.bytes ++
TLV.encodeScript(payoutSPK) ++
satBytes(totalCollateralSatoshis) ++
@ -1211,8 +1322,7 @@ object DLCOfferTLV extends TLVFactory[DLCOfferTLV] {
val contractFlags = iter.take(1).head
val chainHash = iter.take(DoubleSha256Digest, 32)
val contractInfo = iter.take(ContractInfoTLV)
val oracleInfo = iter.take(OracleInfoTLV)
val contractInfo = iter.take(ContractInfoV0TLV)
val fundingPubKey = iter.take(ECPublicKey, 33)
val payoutSPK = iter.takeSPK()
val totalCollateralSatoshis = iter.takeSats()
@ -1227,7 +1337,6 @@ object DLCOfferTLV extends TLVFactory[DLCOfferTLV] {
contractFlags,
chainHash,
contractInfo,
oracleInfo,
fundingPubKey,
payoutSPK,
totalCollateralSatoshis,

View file

@ -82,13 +82,13 @@ class DbManagementTest extends BitcoinSAsyncTest with EmbeddedPg {
val result = dlcDbManagement.migrate()
dlcAppConfig.driver match {
case SQLite =>
val expected = 2
val expected = 1
assert(result == expected)
val flywayInfo = dlcAppConfig.info()
assert(flywayInfo.applied().length == expected)
assert(flywayInfo.pending().length == 0)
case PostgreSQL =>
val expected = 2
val expected = 1
assert(result == expected)
val flywayInfo = dlcAppConfig.info()

View file

@ -263,9 +263,9 @@ class DbCommonsColumnMappers(val profile: JdbcProfile) {
.base[ContractInfo, String](_.hex, ContractInfo.fromHex)
}
implicit val contractInfoTLVMapper: BaseColumnType[ContractInfoTLV] = {
implicit val contractInfoTLVMapper: BaseColumnType[ContractInfoV0TLV] = {
MappedColumnType
.base[ContractInfoTLV, String](_.hex, ContractInfoTLV.fromHex)
.base[ContractInfoV0TLV, String](_.hex, ContractInfoV0TLV.fromHex)
}
implicit val dlcOutcomeTypeMapper: BaseColumnType[DLCOutcomeType] = {

View file

@ -10,9 +10,13 @@ import org.bitcoins.core.currency.{
import org.bitcoins.core.number.{UInt16, UInt32}
import org.bitcoins.core.protocol.BitcoinAddress
import org.bitcoins.core.protocol.BlockStamp.BlockHeight
import org.bitcoins.core.protocol.dlc.DLCMessage.SingleNonceOracleInfo
import org.bitcoins.core.protocol.dlc.DLCMessage.{
ContractInfo,
EnumSingleOracleInfo
}
import org.bitcoins.core.protocol.dlc._
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.tlv.EnumOutcome
import org.bitcoins.core.protocol.transaction.{
Transaction,
TransactionConstants,
@ -305,12 +309,19 @@ class DLCClientIntegrationTest extends BitcoindRpcTest {
val outcomeStrs = DLCTestUtil.genOutcomes(numOutcomes)
val (outcomes, otherOutcomes) =
DLCTestUtil.genContractInfos(outcomeStrs, totalInput)
val oracleInfo = EnumSingleOracleInfo.dummyForKeys(
oraclePrivKey,
preCommittedR,
outcomeStrs.map(EnumOutcome.apply))
val (outcomesDesc, otherOutcomesDesc) =
DLCTestUtil.genContractDescriptors(outcomeStrs, totalInput)
val outcomes = ContractInfo(outcomesDesc, oracleInfo)
val otherOutcomes = ContractInfo(otherOutcomesDesc, oracleInfo)
val acceptDLC = TestDLCClient(
outcomes = outcomes,
oracleInfo = SingleNonceOracleInfo(oraclePubKey, preCommittedR),
isInitiator = false,
fundingPrivKey = localFundingPrivKey,
payoutPrivKey = localPayoutPrivKey,
@ -330,7 +341,6 @@ class DLCClientIntegrationTest extends BitcoindRpcTest {
val offerDLC = TestDLCClient(
outcomes = otherOutcomes,
oracleInfo = SingleNonceOracleInfo(oraclePubKey, preCommittedR),
isInitiator = true,
fundingPrivKey = remoteFundingPrivKey,
payoutPrivKey = remotePayoutPrivKey,

View file

@ -3,14 +3,9 @@ package org.bitcoins.dlc
import org.bitcoins.core.config.RegTest
import org.bitcoins.core.currency.{CurrencyUnit, CurrencyUnits, Satoshis}
import org.bitcoins.core.number.{UInt16, UInt32}
import org.bitcoins.core.protocol.dlc.DLCMessage.{
DLCSign,
MultiNonceContractInfo,
MultiNonceOracleInfo,
SingleNonceOracleInfo
}
import org.bitcoins.core.protocol.dlc._
import org.bitcoins.core.protocol.BlockStamp.BlockTime
import org.bitcoins.core.protocol.dlc.DLCMessage._
import org.bitcoins.core.protocol.dlc._
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.tlv.{
DLCOutcomeType,
@ -29,6 +24,7 @@ import org.bitcoins.dlc.testgen.{DLCTestUtil, TestDLCClient}
import org.bitcoins.dlc.verify.DLCSignatureVerifier
import org.bitcoins.testkit.util.{BitcoinSAsyncTest, BytesUtil}
import org.scalatest.Assertion
import scodec.bits.BitVector
import scala.concurrent.{Future, Promise}
@ -202,30 +198,38 @@ class DLCClientTest extends BitcoinSAsyncTest {
TestDLCClient,
TestDLCClient,
Vector[DLCOutcomeType]) = {
val (outcomes, remoteOutcomes, strsOrDigits) = if (!isMultiNonce) {
val outcomeStrs = DLCTestUtil.genOutcomes(numOutcomes)
val outcomeStrs = DLCTestUtil.genOutcomes(numOutcomes)
val oracleInfo = if (!isMultiNonce) {
EnumSingleOracleInfo.dummyForKeys(oraclePrivKey,
preCommittedR,
outcomeStrs.map(EnumOutcome.apply))
} else {
NumericSingleOracleInfo.dummyForKeys(oraclePrivKey,
preCommittedRs.take(numOutcomes))
}
val (outcomes, remoteOutcomes, strsOrDigits) = if (!isMultiNonce) {
val (localDesc, remoteDesc) =
DLCTestUtil.genContractDescriptors(outcomeStrs, totalInput)
val local = ContractInfo(totalInput.satoshis, localDesc, oracleInfo)
val remote = ContractInfo(totalInput.satoshis, remoteDesc, oracleInfo)
val (local, remote) =
DLCTestUtil.genContractInfos(outcomeStrs, totalInput)
(local, remote, outcomeStrs.map(EnumOutcome.apply))
} else {
val (local, remote) =
val (localDesc, remoteDesc) =
DLCTestUtil.genMultiDigitContractInfo(numOutcomes,
totalInput,
numRounds = 4)
(local, remote, local.allOutcomes)
}
val local = ContractInfo(totalInput.satoshis, localDesc, oracleInfo)
val remote = ContractInfo(totalInput.satoshis, remoteDesc, oracleInfo)
val oracleInfo = if (!isMultiNonce) {
SingleNonceOracleInfo(oraclePubKey, preCommittedR)
} else {
MultiNonceOracleInfo(oraclePubKey, preCommittedRs.take(numOutcomes))
(local, remote, local.allOutcomes)
}
// Offer is local
val dlcOffer: TestDLCClient = TestDLCClient(
outcomes = outcomes,
oracleInfo = oracleInfo,
isInitiator = true,
fundingPrivKey = offerFundingPrivKey,
payoutPrivKey = offerPayoutPrivKey,
@ -246,7 +250,6 @@ class DLCClientTest extends BitcoinSAsyncTest {
// Accept is remote
val dlcAccept: TestDLCClient = TestDLCClient(
outcomes = remoteOutcomes,
oracleInfo = oracleInfo,
isInitiator = false,
fundingPrivKey = acceptFundingPrivKey,
payoutPrivKey = acceptPayoutPrivKey,
@ -380,8 +383,8 @@ class DLCClientTest extends BitcoinSAsyncTest {
}
} else {
val points =
dlcOffer.dlcTxBuilder.oracleAndContractInfo.offerContractInfo
.asInstanceOf[MultiNonceContractInfo]
dlcOffer.dlcTxBuilder.contractInfo.contractDescriptor
.asInstanceOf[NumericContractDescriptor]
.outcomeValueFunc
.points
val left = points(1).outcome
@ -391,7 +394,7 @@ class DLCClientTest extends BitcoinSAsyncTest {
(2 * left + right) / 3 + (outcomeIndex % (right - left) / 3)
val fullDigits =
NumberUtil.decompose(outcomeNum, base = 10, numOutcomes)
NumberUtil.decompose(outcomeNum, base = 2, numOutcomes)
val digits =
CETCalculator.searchForNumericOutcome(fullDigits, outcomes) match {
@ -460,15 +463,16 @@ class DLCClientTest extends BitcoinSAsyncTest {
}
}
val numDigitsToTest: Vector[Int] = Vector(2, 3, 5)
val numDigitsToTest: Vector[Int] = Vector(4, 5, 10)
def runMultiNonceTests(
exec: (Long, Int, Boolean) => Future[Assertion]): Future[Assertion] = {
runTestsForParam(numDigitsToTest) { numDigits =>
val randDigits = (0 until numDigits).toVector.map { _ =>
scala.util.Random.nextInt(10)
scala.util.Random.nextInt(2)
}
val num = randDigits.mkString("").toLong
val num =
BitVector.fromValidBin(randDigits.mkString("")).toLong(signed = false)
exec(num, numDigits, true)
}
@ -486,21 +490,24 @@ class DLCClientTest extends BitcoinSAsyncTest {
val numDigits = 8
val randDigits = (0 until numDigits).toVector.map { _ =>
scala.util.Random.nextInt(10)
scala.util.Random.nextInt(2)
}
val num = randDigits.mkString("").toLong
val num =
BitVector.fromValidBin(randDigits.mkString("")).toLong(signed = false)
executeForCase(num, numDigits, isMultiDigit = true)
}
it should "be able to construct and verify with ScriptInterpreter every tx in a DLC for the refund case" in {
val testFs = numEnumOutcomesToTest.map { numOutcomes =>
executeRefundCase(numOutcomes, isMultiNonce = false).flatMap { _ =>
executeRefundCase(numOutcomes, isMultiNonce = true)
}
val testF1 = numEnumOutcomesToTest.map { numOutcomes =>
executeRefundCase(numOutcomes, isMultiNonce = false)
}
Future.sequence(testFs).map(_ => succeed)
val testF2 = numDigitsToTest.map { numDigits =>
executeRefundCase(numDigits, isMultiNonce = true)
}
Future.sequence(Vector(testF1, testF2).flatten).map(_ => succeed)
}
it should "all work for a 100 outcome DLC" in {
@ -695,12 +702,14 @@ class DLCClientTest extends BitcoinSAsyncTest {
it should "be able to derive aggregate oracle signature from remote CET signatures" in {
// Larger numbers of digits make tests take too long.
// TODO: In the future when bases other than 10 can be used try more digits with base 2
val numDigitsToTest = Vector(2, 3)
val numDigitsToTest = Vector(5, 9)
runTestsForParam(numDigitsToTest) { numDigits =>
val max = (1L << numDigits) - 1
val outcomesToTest = 0
.until(9)
.toVector
.map(num => Vector(num) ++ Vector.fill(numDigits - 1)(0))
.map(num => (max / num.toDouble).toLong)
.map(num => NumberUtil.decompose(num, 2, numDigits))
setupDLC(numDigits, isMultiDigit = true).flatMap {
case (acceptSetup, dlcAccept, offerSetup, dlcOffer, outcomes) =>

View file

@ -1,9 +1,10 @@
package org.bitcoins.dlc.wallet
import org.bitcoins.core.currency.Satoshis
import org.bitcoins.core.protocol.dlc.DLCMessage.{
ContractInfo,
MultiNonceContractInfo,
SingleNonceContractInfo
EnumContractDescriptor,
NumericContractDescriptor
}
import org.bitcoins.core.protocol.dlc.DLCState
import org.bitcoins.core.protocol.dlc.DLCStatus.{
@ -11,7 +12,6 @@ import org.bitcoins.core.protocol.dlc.DLCStatus.{
Refunded,
RemoteClaimed
}
import org.bitcoins.core.currency.Satoshis
import org.bitcoins.core.script.interpreter.ScriptInterpreter
import org.bitcoins.crypto.{CryptoUtil, SchnorrDigitalSignature}
import org.bitcoins.testkit.wallet.DLCWalletUtil._
@ -30,15 +30,15 @@ class DLCExecutionTest extends BitcoinSDualWalletTest {
def getSigs(contractInfo: ContractInfo): (
SchnorrDigitalSignature,
SchnorrDigitalSignature) = {
val info: SingleNonceContractInfo = contractInfo match {
case info: SingleNonceContractInfo => info
case _: MultiNonceContractInfo =>
val desc: EnumContractDescriptor = contractInfo.contractDescriptor match {
case desc: EnumContractDescriptor => desc
case _: NumericContractDescriptor =>
throw new IllegalArgumentException("Unexpected Contract Info")
}
// Get a hash that the initiator wins for
val initiatorWinStr =
info
desc
.maxBy(_._2.toLong)
._1
.outcome
@ -50,7 +50,7 @@ class DLCExecutionTest extends BitcoinSDualWalletTest {
// Get a hash that the recipient wins for
val recipientWinStr =
info.find(_._2 == Satoshis.zero).get._1.outcome
desc.find(_._2 == Satoshis.zero).get._1.outcome
val recipientWinSig = DLCWalletUtil.oraclePrivKey
.schnorrSignWithNonce(CryptoUtil
.sha256DLCAttestation(recipientWinStr)

View file

@ -3,8 +3,8 @@ package org.bitcoins.dlc.wallet
import org.bitcoins.core.currency.Satoshis
import org.bitcoins.core.protocol.dlc.DLCMessage.{
ContractInfo,
MultiNonceContractInfo,
SingleNonceContractInfo
EnumContractDescriptor,
NumericContractDescriptor
}
import org.bitcoins.core.protocol.dlc.DLCState
import org.bitcoins.core.protocol.dlc.DLCStatus.{Claimed, RemoteClaimed}
@ -26,14 +26,14 @@ class DLCMultiNonceExecutionTest extends BitcoinSDualWalletTest {
def getSigs(contractInfo: ContractInfo): (
Vector[SchnorrDigitalSignature],
Vector[SchnorrDigitalSignature]) = {
val multiNonceContractInfo: MultiNonceContractInfo = contractInfo match {
case info: MultiNonceContractInfo => info
case _: SingleNonceContractInfo =>
contractInfo.contractDescriptor match {
case _: NumericContractDescriptor => ()
case _: EnumContractDescriptor =>
throw new IllegalArgumentException("Unexpected Contract Info")
}
val initiatorWinVec =
multiNonceContractInfo.outcomeVec
contractInfo.outcomeVecOpt.get
.maxBy(_._2.toLong)
._1
@ -49,7 +49,7 @@ class DLCMultiNonceExecutionTest extends BitcoinSDualWalletTest {
}
val recipientWinVec =
multiNonceContractInfo.outcomeVec.find(_._2 == Satoshis.zero).get._1
contractInfo.outcomeVecOpt.get.find(_._2 == Satoshis.zero).get._1
val kValues2 = DLCWalletUtil.kValues.take(recipientWinVec.size)

View file

@ -3,6 +3,7 @@ package org.bitcoins.dlc.wallet
import org.bitcoins.core.currency.Satoshis
import org.bitcoins.core.protocol.dlc.DLCMessage._
import org.bitcoins.core.protocol.dlc._
import org.bitcoins.core.protocol.tlv._
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
import org.bitcoins.crypto._
import org.bitcoins.testkit.wallet.DLCWalletUtil._
@ -29,7 +30,6 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
for {
offer <- walletA.createDLCOffer(
offerData.oracleInfo,
offerData.contractInfo,
offerData.totalCollateral,
Some(offerData.feeRate),
@ -130,7 +130,6 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
for {
offer <- walletA.createDLCOffer(
offerData.oracleInfo,
offerData.contractInfo.toTLV,
offerData.totalCollateral,
Some(offerData.feeRate),
@ -226,7 +225,6 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
offerData: DLCOffer = DLCWalletUtil.sampleDLCOffer): Future[DLCAccept] = {
for {
offer <- walletA.createDLCOffer(
offerData.oracleInfo,
offerData.contractInfo,
offerData.totalCollateral,
Some(offerData.feeRate),
@ -343,23 +341,17 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
val betSize = 10000
lazy val contractInfo: ContractInfo =
SingleNonceContractInfo.fromStringVec(
val contractDescriptor: EnumContractDescriptor =
EnumContractDescriptor.fromStringVec(
Vector(winStr -> Satoshis(betSize),
loseStr -> Satoshis.zero,
drawStr -> Satoshis(betSize / 2)))
val oraclePubKey = SchnorrPublicKey(
"bd25c9f473eb5d37e5299b86f2d7b625d01ae0441da428cc65cf19e1c6021db6")
val oracleNonce = SchnorrNonce(
"d535016b6d837587ac6375d0044cb60292afb9ca6763483595eb2909c91063af")
val attestation = FieldElement(
"413c05252d4003cd20f0a0901da193d0c7b53b7c6c3e3426e6f106a0ef96e18f")
val oracleInfo = SingleNonceOracleInfo(oraclePubKey, oracleNonce)
val oracleInfo = EnumSingleOracleInfo(OracleAnnouncementTLV(
"fdd824b4caaec7479cc9d37003f5add6504d035054ffeac8637a990305a45cfecc1062044c3f68b45318f57e41c4544a4a950c0744e2a80854349a3426b00ad86da5090b9e942dc6df2ae87f007b45b0ccd63e6c354d92c4545fc099ea3e137e54492d1efdd822500001a6a09c7c83c50b34f9db560a2e14fef2eab5224c15b18c7114331756364bfce65ffe3800fdd8062400030c44656d6f637261745f77696e0e52657075626c6963616e5f77696e056f746865720161"))
val offerData = DLCOffer(
OracleAndContractInfo(oracleInfo, contractInfo),
ContractInfo(contractDescriptor, oracleInfo),
dummyDLCKeys,
Satoshis(5000),
Vector(dummyFundingInputs.head),
@ -368,15 +360,14 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
dummyTimeouts
)
val oracleSig = SchnorrDigitalSignature(oracleNonce, attestation)
val oracleSig = SchnorrDigitalSignature(
"a6a09c7c83c50b34f9db560a2e14fef2eab5224c15b18c7114331756364bfce6c59736cdcfe1e0a89064f846d5dbde0902f82688dde34dc1833965a60240f287")
val paramHash = DLCMessage.calcParamHash(offerData.oracleInfo,
offerData.contractInfo,
offerData.timeouts)
val paramHash =
DLCMessage.calcParamHash(offerData.contractInfo, offerData.timeouts)
for {
offer <- walletA.createDLCOffer(
offerData.oracleInfo,
offerData.contractInfo,
offerData.totalCollateral,
Some(offerData.feeRate),

View file

@ -11,6 +11,7 @@ CREATE TABLE "wallet_dlcs"
"funding_outpoint" TEXT,
"funding_tx_id" TEXT,
"closing_tx_id" TEXT,
"outcome" TEXT,
constraint "pk_dlc" primary key ("param_hash")
);
CREATE INDEX "wallet_dlcs_param_hash_index" on "wallet_dlcs" ("param_hash");
@ -19,7 +20,6 @@ CREATE TABLE "wallet_dlc_offers"
(
"param_hash" VARCHAR(254) NOT NULL UNIQUE,
"temp_contract_id" TEXT NOT NULL UNIQUE,
"oracle_info_tlv" TEXT NOT NULL,
"contract_info" TEXT NOT NULL,
"contract_maturity" TEXT NOT NULL,
"contract_timeout" TEXT NOT NULL,

View file

@ -1 +0,0 @@
ALTER TABLE "wallet_dlcs" ADD COLUMN "outcome" TEXT;

View file

@ -10,7 +10,8 @@ CREATE TABLE "wallet_dlcs"
"oracle_sigs" VARCHAR(254),
"funding_outpoint" VARCHAR(254),
"funding_tx_id" VARCHAR(254),
"closing_tx_id" VARCHAR(254)
"closing_tx_id" VARCHAR(254),
"outcome" VARCHAR(254)
);
CREATE INDEX "wallet_dlcs_param_hash_index" on "wallet_dlcs" ("param_hash");
@ -18,7 +19,6 @@ CREATE TABLE "wallet_dlc_offers"
(
"param_hash" VARCHAR(254) NOT NULL UNIQUE,
"temp_contract_id" VARCHAR(254) NOT NULL UNIQUE,
"oracle_info_tlv" VARCHAR(254) NOT NULL,
"contract_info" VARCHAR(254) NOT NULL,
"contract_maturity" VARCHAR(254) NOT NULL,
"contract_timeout" VARCHAR(254) NOT NULL,

View file

@ -1 +0,0 @@
ALTER TABLE "wallet_dlcs" ADD COLUMN "outcome" TEXT;

View file

@ -396,7 +396,6 @@ abstract class DLCWallet extends Wallet with AnyDLCHDWalletApi {
* This is the first step of the initiator
*/
override def createDLCOffer(
oracleInfo: OracleInfo,
contractInfo: ContractInfo,
collateral: Satoshis,
feeRateOpt: Option[FeeUnit],
@ -407,7 +406,7 @@ abstract class DLCWallet extends Wallet with AnyDLCHDWalletApi {
val timeouts =
DLCTimeouts(BlockStamp(locktime.toInt), BlockStamp(refundLocktime.toInt))
val paramHash = DLCMessage.calcParamHash(oracleInfo, contractInfo, timeouts)
val paramHash = DLCMessage.calcParamHash(contractInfo, timeouts)
logger.debug(
s"Checking if DLC Offer has already been made (${paramHash.hex})")
@ -433,7 +432,6 @@ abstract class DLCWallet extends Wallet with AnyDLCHDWalletApi {
case None =>
createNewDLCOffer(
collateral = collateral,
oracleInfo = oracleInfo,
contractInfo = contractInfo,
feeRate = satoshisPerVirtualByte,
timeouts = timeouts
@ -444,12 +442,11 @@ abstract class DLCWallet extends Wallet with AnyDLCHDWalletApi {
private def createNewDLCOffer(
collateral: CurrencyUnit,
oracleInfo: OracleInfo,
contractInfo: ContractInfo,
feeRate: SatoshisPerVirtualByte,
timeouts: DLCTimeouts): Future[DLCOffer] = {
logger.info("Creating DLC Offer")
val paramHash = DLCMessage.calcParamHash(oracleInfo, contractInfo, timeouts)
val paramHash = DLCMessage.calcParamHash(contractInfo, timeouts)
for {
account <- getDefaultAccountForType(AddressType.SegWit)
@ -476,7 +473,7 @@ abstract class DLCWallet extends Wallet with AnyDLCHDWalletApi {
_ = logger.debug(
s"DLC Offer data collected, creating database entry, ${paramHash.hex}")
offer = DLCOffer(OracleAndContractInfo(oracleInfo, contractInfo),
offer = DLCOffer(contractInfo,
dlcPubKeys,
collateral.satoshis,
utxos,
@ -1451,7 +1448,6 @@ abstract class DLCWallet extends Wallet with AnyDLCHDWalletApi {
paramHash,
dlcDb.isInitiator,
dlcDb.tempContractId,
offerDb.oracleInfo,
offerDb.contractInfo,
offerDb.dlcTimeouts,
offerDb.feeRate,
@ -1464,7 +1460,6 @@ abstract class DLCWallet extends Wallet with AnyDLCHDWalletApi {
dlcDb.isInitiator,
dlcDb.tempContractId,
dlcDb.contractIdOpt.get,
offerDb.oracleInfo,
offerDb.contractInfo,
offerDb.dlcTimeouts,
offerDb.feeRate,
@ -1477,7 +1472,6 @@ abstract class DLCWallet extends Wallet with AnyDLCHDWalletApi {
dlcDb.isInitiator,
dlcDb.tempContractId,
dlcDb.contractIdOpt.get,
offerDb.oracleInfo,
offerDb.contractInfo,
offerDb.dlcTimeouts,
offerDb.feeRate,
@ -1490,7 +1484,6 @@ abstract class DLCWallet extends Wallet with AnyDLCHDWalletApi {
dlcDb.isInitiator,
dlcDb.tempContractId,
dlcDb.contractIdOpt.get,
offerDb.oracleInfo,
offerDb.contractInfo,
offerDb.dlcTimeouts,
offerDb.feeRate,
@ -1504,7 +1497,6 @@ abstract class DLCWallet extends Wallet with AnyDLCHDWalletApi {
dlcDb.isInitiator,
dlcDb.tempContractId,
dlcDb.contractIdOpt.get,
offerDb.oracleInfo,
offerDb.contractInfo,
offerDb.dlcTimeouts,
offerDb.feeRate,
@ -1518,7 +1510,6 @@ abstract class DLCWallet extends Wallet with AnyDLCHDWalletApi {
dlcDb.isInitiator,
dlcDb.tempContractId,
dlcDb.contractIdOpt.get,
offerDb.oracleInfo,
offerDb.contractInfo,
offerDb.dlcTimeouts,
offerDb.feeRate,
@ -1539,7 +1530,6 @@ abstract class DLCWallet extends Wallet with AnyDLCHDWalletApi {
dlcDb.isInitiator,
dlcDb.tempContractId,
dlcDb.contractIdOpt.get,
offerDb.oracleInfo,
offerDb.contractInfo,
offerDb.dlcTimeouts,
offerDb.feeRate,
@ -1556,7 +1546,6 @@ abstract class DLCWallet extends Wallet with AnyDLCHDWalletApi {
dlcDb.isInitiator,
dlcDb.tempContractId,
dlcDb.contractIdOpt.get,
offerDb.oracleInfo,
offerDb.contractInfo,
offerDb.dlcTimeouts,
offerDb.feeRate,

View file

@ -17,23 +17,16 @@ import scala.concurrent.Future
trait DLCWalletApi { self: WalletApi =>
def createDLCOffer(
oracleInfo: OracleInfo,
contractInfoTLV: ContractInfoTLV,
contractInfoTLV: ContractInfoV0TLV,
collateral: Satoshis,
feeRateOpt: Option[FeeUnit],
locktime: UInt32,
refundLT: UInt32): Future[DLCOffer] = {
val contractInfo = ContractInfo.fromTLV(contractInfoTLV)
createDLCOffer(oracleInfo,
contractInfo,
collateral,
feeRateOpt,
locktime,
refundLT)
createDLCOffer(contractInfo, collateral, feeRateOpt, locktime, refundLT)
}
def createDLCOffer(
oracleInfo: OracleInfo,
contractInfo: ContractInfo,
collateral: Satoshis,
feeRateOpt: Option[FeeUnit],
@ -42,7 +35,6 @@ trait DLCWalletApi { self: WalletApi =>
def registerDLCOffer(dlcOffer: DLCOffer): Future[DLCOffer] = {
createDLCOffer(
dlcOffer.oracleInfo,
dlcOffer.contractInfo,
dlcOffer.totalCollateral,
Some(dlcOffer.feeRate),

View file

@ -1,7 +1,7 @@
package org.bitcoins.dlc.wallet.models
import org.bitcoins.core.currency.CurrencyUnit
import org.bitcoins.core.protocol.tlv.{ContractInfoTLV, OracleInfoTLV}
import org.bitcoins.core.protocol.tlv.ContractInfoV0TLV
import org.bitcoins.core.protocol.{BitcoinAddress, BlockTimeStamp}
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
import org.bitcoins.crypto._
@ -68,9 +68,7 @@ case class DLCOfferDAO()(implicit
def tempContractId: Rep[Sha256Digest] =
column("temp_contract_id", O.Unique)
def oracleInfoTLV: Rep[OracleInfoTLV] = column("oracle_info_tlv")
def contractInfoTLV: Rep[ContractInfoTLV] = column("contract_info")
def contractInfoTLV: Rep[ContractInfoV0TLV] = column("contract_info")
def contractMaturity: Rep[BlockTimeStamp] = column("contract_maturity")
@ -89,7 +87,6 @@ case class DLCOfferDAO()(implicit
def * : ProvenShape[DLCOfferDb] =
(paramHash,
tempContractId,
oracleInfoTLV,
contractInfoTLV,
contractMaturity,
contractTimeout,

View file

@ -7,7 +7,7 @@ import org.bitcoins.core.protocol.dlc.{
DLCPublicKeys,
DLCTimeouts
}
import org.bitcoins.core.protocol.tlv.{ContractInfoTLV, OracleInfoTLV}
import org.bitcoins.core.protocol.tlv.ContractInfoV0TLV
import org.bitcoins.core.protocol.{BitcoinAddress, BlockTimeStamp}
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
import org.bitcoins.crypto._
@ -15,8 +15,7 @@ import org.bitcoins.crypto._
case class DLCOfferDb(
paramHash: Sha256DigestBE,
tempContractId: Sha256Digest,
oracleInfoTLV: OracleInfoTLV,
contractInfoTLV: ContractInfoTLV,
contractInfoTLV: ContractInfoV0TLV,
contractMaturity: BlockTimeStamp,
contractTimeout: BlockTimeStamp,
fundingKey: ECPublicKey,
@ -25,10 +24,10 @@ case class DLCOfferDb(
feeRate: SatoshisPerVirtualByte,
changeAddress: BitcoinAddress) {
lazy val oracleInfo: OracleInfo = OracleInfo.fromTLV(oracleInfoTLV)
lazy val contractInfo: ContractInfo = ContractInfo.fromTLV(contractInfoTLV)
lazy val oracleInfo: OracleInfo = contractInfo.oracleInfo
lazy val dlcPubKeys: DLCPublicKeys = DLCPublicKeys(fundingKey, payoutAddress)
lazy val dlcTimeouts: DLCTimeouts =
@ -37,7 +36,7 @@ case class DLCOfferDb(
def toDLCOffer(fundingInputs: Vector[DLCFundingInput]): DLCOffer = {
DLCOffer(
OracleAndContractInfo(oracleInfo, contractInfo),
contractInfo,
dlcPubKeys,
totalCollateral.satoshis,
fundingInputs,
@ -54,7 +53,6 @@ object DLCOfferDbHelper {
DLCOfferDb(
offer.paramHash,
offer.tempContractId,
offer.oracleInfo.toTLV,
offer.contractInfo.toTLV,
offer.timeouts.contractMaturity,
offer.timeouts.contractTimeout,

View file

@ -1,6 +1,6 @@
package org.bitcoins.dlc.builder
import org.bitcoins.core.protocol.dlc.DLCMessage.OracleAndContractInfo
import org.bitcoins.core.protocol.dlc.DLCMessage.ContractInfo
import org.bitcoins.core.protocol.dlc.DLCTimeouts
import org.bitcoins.core.protocol.script.{
EmptyScriptSignature,
@ -25,7 +25,7 @@ import scala.concurrent.{ExecutionContext, Future}
* Contract Execution Transactions (CETs)
*/
case class DLCCETBuilder(
oracleAndContractInfo: OracleAndContractInfo,
contractInfo: ContractInfo,
offerFundingKey: ECPublicKey,
offerFinalSPK: ScriptPubKey,
acceptFundingKey: ECPublicKey,
@ -53,7 +53,7 @@ case class DLCCETBuilder(
ec: ExecutionContext): Future[WitnessTransaction] = {
val builder = RawTxBuilder().setLockTime(timeouts.contractMaturity.toUInt32)
val (offerValue, acceptValue) = oracleAndContractInfo.getPayouts(msg)
val (offerValue, acceptValue) = contractInfo.getPayouts(msg)
builder += TransactionOutput(offerValue, offerFinalSPK)
builder += TransactionOutput(acceptValue, acceptFinalSPK)

View file

@ -53,12 +53,12 @@ case class DLCTxBuilder(offer: DLCOffer, accept: DLCAcceptWithoutSigs)(implicit
// builder.offer.oracleAndContractInfo should not be used,
// builder.oracleAndContractInfo should be used instead in case a party
// is over-collateralized in which case payouts will be incorrect here.
private val oracleAndContractInfoBeforeAccept: OracleAndContractInfo =
offer.oracleAndContractInfo
private val contractInfoBeforeAccept: ContractInfo =
offer.contractInfo
val oracleAndContractInfo: OracleAndContractInfo =
oracleAndContractInfoBeforeAccept.updateOnAccept(totalInput.satoshis,
acceptNegotiationFields)
val contractInfo: ContractInfo =
contractInfoBeforeAccept.updateOnAccept(totalInput.satoshis,
acceptNegotiationFields)
val offerTotalFunding: CurrencyUnit =
offerFundingInputs.map(_.output.value).sum
@ -74,7 +74,7 @@ case class DLCTxBuilder(offer: DLCOffer, accept: DLCAcceptWithoutSigs)(implicit
"Offer change address must have same network as final address")
require(acceptChangeAddress.networkParameters == network,
"Accept change address must have same network as final address")
require(totalInput >= oracleAndContractInfo.offerContractInfo.max,
require(totalInput >= contractInfo.max,
"Total collateral must add up to max winnings")
require(
offerTotalFunding >= offerTotalCollateral,
@ -87,7 +87,7 @@ case class DLCTxBuilder(offer: DLCOffer, accept: DLCAcceptWithoutSigs)(implicit
def getPayouts(oracleSigs: Vector[SchnorrDigitalSignature]): (
CurrencyUnit,
CurrencyUnit) = {
oracleAndContractInfo.getPayouts(oracleSigs)
contractInfo.getPayouts(oracleSigs)
}
lazy val fundingTxBuilder: DLCFundingTxBuilder = {
@ -124,7 +124,7 @@ case class DLCTxBuilder(offer: DLCOffer, accept: DLCAcceptWithoutSigs)(implicit
OutputReference(fundingOutPoint, fundingTx.outputs.head)
DLCCETBuilder(
oracleAndContractInfo = oracleAndContractInfo,
contractInfo = contractInfo,
offerFundingKey = offerFundingKey,
offerFinalSPK = offerFinalAddress.scriptPubKey,
acceptFundingKey = acceptFundingKey,

View file

@ -78,7 +78,7 @@ case class DLCExecutor(signer: DLCTxSigner)(implicit ec: ExecutionContext) {
ExecutedDLCOutcome] = {
val SetupDLC(fundingTx, cetInfos, _) = dlcSetup
val msgOpt = builder.oracleAndContractInfo.findOutcome(oracleSigs)
val msgOpt = builder.contractInfo.findOutcome(oracleSigs)
val (msg, remoteAdaptorSig) = msgOpt match {
case Some(msg) =>
val cetInfo = cetInfos(msg)

View file

@ -181,7 +181,7 @@ case class DLCTxSigner(
/** Signs remote's Contract Execution Transaction (CET) for a given outcome hash */
def createRemoteCETSig(msg: DLCOutcomeType): Future[ECAdaptorSignature] = {
val adaptorPoint = builder.oracleAndContractInfo.sigPointForOutcome(msg)
val adaptorPoint = builder.contractInfo.sigPointForOutcome(msg)
val hashType = HashType.sigHashAll
for {
fundingTx <- builder.buildFundingTx
@ -281,7 +281,7 @@ case class DLCTxSigner(
/** Creates all of this party's CETSignatures */
def createCETSigs(): Future[CETSignatures] = {
val cetSigFs = builder.oracleAndContractInfo.allOutcomes.map { msg =>
val cetSigFs = builder.contractInfo.allOutcomes.map { msg =>
// Need to wrap in another future so they are all started at once
// and do not block each other
Future(createRemoteCETSig(msg).map(msg -> _)).flatten

View file

@ -149,19 +149,6 @@ object DLCParsingTestVector extends TestVectorParser[DLCParsingTestVector] {
def apply(tlv: TLV): DLCParsingTestVector = {
tlv match {
case ContractInfoV0TLV(outcomes) =>
val fields = Vector(
"tpe" -> Element(ContractInfoV0TLV.tpe),
"length" -> Element(tlv.length),
"outcomes" -> MultiElement(outcomes.map {
case (outcome, amt) =>
NamedMultiElement("outcome" -> CryptoUtil
.sha256DLCAttestation(outcome)
.bytes,
"localPayout" -> amt.toUInt64.bytes)
})
)
DLCTLVTestVector(tlv, "contract_info_v0", fields)
case PayoutFunctionV0TLV(points) =>
val fields = Vector(
"tpe" -> Element(PayoutFunctionV0TLV.tpe),
@ -190,37 +177,71 @@ object DLCParsingTestVector extends TestVectorParser[DLCParsingTestVector] {
})
)
DLCTLVTestVector(tlv, "rounding_intervals_v0", fields)
case ContractInfoV1TLV(base,
numDigits,
totalCollateral,
payoutFunction,
roundingIntervals) =>
case ContractDescriptorV0TLV(outcomes) =>
val fields = Vector(
"tpe" -> Element(ContractInfoV1TLV.tpe),
"tpe" -> Element(ContractDescriptorV0TLV.tpe),
"length" -> Element(tlv.length),
"outcomes" -> MultiElement(outcomes.map {
case (outcome, amt) =>
NamedMultiElement("outcome" -> CryptoUtil.sha256(outcome).bytes,
"localPayout" -> amt.toUInt64.bytes)
})
)
DLCTLVTestVector(tlv, "contract_descriptor_v0", fields)
case ContractDescriptorV1TLV(numDigits,
payoutFunction,
roundingIntervals) =>
val fields = Vector(
"tpe" -> Element(ContractDescriptorV1TLV.tpe),
"length" -> Element(tlv.length),
"base" -> Element(BigSizeUInt(base)),
"numDigits" -> Element(UInt16(numDigits)),
"totalCollateral" -> Element(UInt64(totalCollateral.toLong)),
"payoutFunction" -> Element(payoutFunction),
"roundingIntervals" -> Element(roundingIntervals)
)
DLCTLVTestVector(tlv, "contract_info_v1", fields)
case OracleInfoV0TLV(pubKey, rValue) =>
DLCTLVTestVector(tlv, "contract_descriptor_v1", fields)
case OracleInfoV0TLV(announcement) =>
val fields = Vector(
"tpe" -> Element(OracleInfoV0TLV.tpe),
"length" -> Element(tlv.length),
"pubKey" -> Element(pubKey),
"rValue" -> Element(rValue)
"announcement" -> Element(announcement)
)
DLCTLVTestVector(tlv, "oracle_info_v0", fields)
case OracleInfoV1TLV(pubKey, nonces) =>
case OracleParamsV0TLV(maxErrorExp, minFailExp, maximizeCoverage) =>
val maximizeCoverageBytes = ByteVector(
if (maximizeCoverage) TLV.TRUE_BYTE else TLV.FALSE_BYTE)
val fields = Vector(
"tpe" -> Element(OracleParamsV0TLV.tpe),
"length" -> Element(tlv.length),
"maxErrorExp" -> Element(UInt16(maxErrorExp)),
"minFailExp" -> Element(UInt16(minFailExp)),
"maximizeCoverage" -> Element(maximizeCoverageBytes)
)
DLCTLVTestVector(tlv, "oracle_params_v0", fields)
case OracleInfoV1TLV(announcements) =>
val fields = Vector(
"tpe" -> Element(OracleInfoV1TLV.tpe),
"length" -> Element(tlv.length),
"pubKey" -> Element(pubKey),
"nonces" -> MultiElement(nonces.map(Element(_)))
"announcements" -> MultiElement(announcements.map(Element(_)))
)
DLCTLVTestVector(tlv, "oracle_info_v1", fields)
case OracleInfoV2TLV(oracles, params) =>
val fields = Vector(
"tpe" -> Element(OracleInfoV2TLV.tpe),
"length" -> Element(tlv.length),
"announcements" -> MultiElement(oracles.map(Element(_))),
"params" -> Element(params)
)
DLCTLVTestVector(tlv, "oracle_info_v2", fields)
case ContractInfoV0TLV(totalCollateral, contractDescriptor, oracleInfo) =>
val fields = Vector(
"tpe" -> Element(ContractInfoV0TLV.tpe),
"length" -> Element(tlv.length),
"totalCollateral" -> Element(totalCollateral.toUInt64),
"contractDescriptor" -> Element(contractDescriptor),
"oracleInfo" -> Element(oracleInfo)
)
DLCTLVTestVector(tlv, "contract_info_v0", fields)
case FundingInputV0TLV(prevTx,
prevTxVout,
sequence,
@ -272,7 +293,6 @@ object DLCParsingTestVector extends TestVectorParser[DLCParsingTestVector] {
case DLCOfferTLV(contractFlags,
chainHash,
contractInfo,
oracleInfo,
fundingPubKey,
payoutSPK,
totalCollateralSatoshis,
@ -286,7 +306,6 @@ object DLCParsingTestVector extends TestVectorParser[DLCParsingTestVector] {
"contractFlags" -> Element(ByteVector(contractFlags)),
"chainHash" -> Element(chainHash),
"contractInfo" -> Element(contractInfo),
"oracleInfo" -> Element(oracleInfo),
"fundingPubKey" -> Element(fundingPubKey),
"payoutSPKLen" -> Element(UInt16(payoutSPK.asmBytes.length)),
"payoutSPK" -> Element(payoutSPK.asmBytes),

View file

@ -28,7 +28,7 @@ object DLCParsingTestVectorGen
override def generateTestVectors(): Future[Vector[DLCParsingTestVector]] = {
Future.successful(
Vector(
DLCTLVGen.contractInfoParsingTestVector(),
DLCTLVGen.contractDescriptorParsingTestVector(),
DLCTLVGen.oracleInfoParsingTestVector(),
DLCTLVGen.fundingInputParsingTestVector(),
DLCTLVGen.cetSigsParsingTestVector(),

View file

@ -24,42 +24,56 @@ object DLCTLVGen {
CryptoUtil.sha256(bytes)
}
def genContractInfo(
def genContractDescriptor(
outcomes: Vector[String] = DLCTestUtil.genOutcomes(3),
totalInput: CurrencyUnit = defaultAmt * 2): SingleNonceContractInfo = {
DLCTestUtil.genContractInfos(outcomes, totalInput)._1
totalInput: CurrencyUnit = defaultAmt * 2): EnumContractDescriptor = {
DLCTestUtil.genContractDescriptors(outcomes, totalInput)._1
}
def contractInfoParsingTestVector(
def contractDescriptorParsingTestVector(
outcomes: Vector[String] = DLCTestUtil.genOutcomes(3),
totalInput: CurrencyUnit = defaultAmt * 2): DLCParsingTestVector = {
DLCParsingTestVector(genContractInfo(outcomes, totalInput).toTLV)
DLCParsingTestVector(genContractDescriptor(outcomes, totalInput).toTLV)
}
def genOracleInfo(
oraclePubKey: SchnorrPublicKey =
ECPublicKey.freshPublicKey.schnorrPublicKey,
oracleRValue: SchnorrNonce =
ECPublicKey.freshPublicKey.schnorrNonce): SingleNonceOracleInfo = {
SingleNonceOracleInfo(oraclePubKey, oracleRValue)
oraclePrivKey: ECPrivateKey = ECPrivateKey.freshPrivateKey,
oracleRValue: SchnorrNonce = ECPublicKey.freshPublicKey.schnorrNonce,
events: Vector[String] =
Vector("dummy1", "dummy2")): EnumSingleOracleInfo = {
EnumSingleOracleInfo(
OracleAnnouncementV0TLV.dummyForEventsAndKeys(
oraclePrivKey,
oracleRValue,
events.map(EnumOutcome.apply)))
}
def genOracleAndContractInfo(
oraclePubKey: SchnorrPublicKey =
ECPublicKey.freshPublicKey.schnorrPublicKey,
def genContractInfo(
oraclePrivKey: ECPrivateKey = ECPrivateKey.freshPrivateKey,
oracleRValue: SchnorrNonce = ECPublicKey.freshPublicKey.schnorrNonce,
outcomes: Vector[String] = DLCTestUtil.genOutcomes(3),
totalInput: CurrencyUnit = defaultAmt * 2): OracleAndContractInfo = {
OracleAndContractInfo(genOracleInfo(oraclePubKey, oracleRValue),
genContractInfo(outcomes, totalInput))
totalInput: CurrencyUnit = defaultAmt * 2): ContractInfo = {
ContractInfo(totalInput.satoshis,
genContractDescriptor(outcomes, totalInput),
genOracleInfo(oraclePrivKey, oracleRValue, outcomes))
}
def contractInfoParsingTestVector(
oraclePrivKey: ECPrivateKey = ECPrivateKey.freshPrivateKey,
oracleRValue: SchnorrNonce = ECPublicKey.freshPublicKey.schnorrNonce,
outcomes: Vector[String] = DLCTestUtil.genOutcomes(3),
totalInput: CurrencyUnit = defaultAmt * 2): DLCParsingTestVector = {
DLCParsingTestVector(
genContractInfo(oraclePrivKey, oracleRValue, outcomes, totalInput).toTLV)
}
def oracleInfoParsingTestVector(
oraclePubKey: SchnorrPublicKey =
ECPublicKey.freshPublicKey.schnorrPublicKey,
oracleRValue: SchnorrNonce =
ECPublicKey.freshPublicKey.schnorrNonce): DLCParsingTestVector = {
DLCParsingTestVector(genOracleInfo(oraclePubKey, oracleRValue).toTLV)
oraclePrivKey: ECPrivateKey = ECPrivateKey.freshPrivateKey,
oracleRValue: SchnorrNonce = ECPublicKey.freshPublicKey.schnorrNonce,
events: Vector[String] =
Vector("dummy1", "dummy2")): DLCParsingTestVector = {
DLCParsingTestVector(
genOracleInfo(oraclePrivKey, oracleRValue, events).toTLV)
}
def p2wpkh(
@ -188,7 +202,7 @@ object DLCTLVGen {
}
def dlcOffer(
oracleAndContractInfo: OracleAndContractInfo = genOracleAndContractInfo(),
contractInfo: ContractInfo = genContractInfo(),
fundingPubKey: ECPublicKey = ECPublicKey.freshPublicKey,
payoutAddress: BitcoinAddress = address(),
totalCollateral: Satoshis = defaultAmt,
@ -198,7 +212,7 @@ object DLCTLVGen {
contractMaturityBound: BlockTimeStamp = BlockTimeStamp(100),
contractTimeout: BlockTimeStamp = BlockTimeStamp(200)): DLCOffer = {
DLCOffer(
oracleAndContractInfo,
contractInfo,
DLCPublicKeys(fundingPubKey, payoutAddress),
totalCollateral,
fundingInputs,
@ -209,7 +223,7 @@ object DLCTLVGen {
}
def dlcOfferTLV(
oracleAndContractInfo: OracleAndContractInfo = genOracleAndContractInfo(),
contractInfo: ContractInfo = genContractInfo(),
fundingPubKey: ECPublicKey = ECPublicKey.freshPublicKey,
payoutAddress: BitcoinAddress = address(),
totalCollateral: Satoshis = defaultAmt,
@ -218,7 +232,7 @@ object DLCTLVGen {
feeRate: SatoshisPerVirtualByte = SatoshisPerVirtualByte.one,
contractMaturityBound: BlockTimeStamp = BlockTimeStamp(100),
contractTimeout: BlockTimeStamp = BlockTimeStamp(200)): DLCOfferTLV = {
dlcOffer(oracleAndContractInfo,
dlcOffer(contractInfo,
fundingPubKey,
payoutAddress,
totalCollateral,
@ -230,7 +244,7 @@ object DLCTLVGen {
}
def dlcOfferParsingTestVector(
oracleAndContractInfo: OracleAndContractInfo = genOracleAndContractInfo(),
contractInfo: ContractInfo = genContractInfo(),
fundingPubKey: ECPublicKey = ECPublicKey.freshPublicKey,
payoutAddress: BitcoinAddress = address(),
totalCollateral: Satoshis = defaultAmt,
@ -241,7 +255,7 @@ object DLCTLVGen {
contractTimeout: BlockTimeStamp =
BlockTimeStamp(200)): DLCParsingTestVector = {
DLCParsingTestVector(
dlcOfferTLV(oracleAndContractInfo,
dlcOfferTLV(contractInfo,
fundingPubKey,
payoutAddress,
totalCollateral,
@ -315,7 +329,7 @@ object DLCTLVGen {
offer.contractInfo.max - offer.totalCollateral + overCollateral
val cetSignatures =
cetSigs(offer.oracleAndContractInfo.allOutcomes, fundingPubKey)
cetSigs(offer.contractInfo.allOutcomes, fundingPubKey)
val tempContractId = offer.tempContractId
@ -371,7 +385,7 @@ object DLCTLVGen {
offer: DLCOffer,
contractId: ByteVector = hash().bytes): DLCSign = {
val cetSignatures =
cetSigs(offer.oracleAndContractInfo.allOutcomes, offer.pubKeys.fundingKey)
cetSigs(offer.contractInfo.allOutcomes, offer.pubKeys.fundingKey)
val fundingSignatures = fundingSigs(offer.fundingInputs.map(_.outPoint))
DLCSign(cetSignatures, fundingSignatures, contractId)
}

View file

@ -2,8 +2,8 @@ package org.bitcoins.dlc.testgen
import org.bitcoins.core.currency.{CurrencyUnit, Satoshis}
import org.bitcoins.core.protocol.dlc.DLCMessage.{
MultiNonceContractInfo,
SingleNonceContractInfo
EnumContractDescriptor,
NumericContractDescriptor
}
import org.bitcoins.core.protocol.dlc.{
DLCPayoutCurve,
@ -35,15 +35,17 @@ object DLCTestUtil {
valsWithOrder.sortBy(_._2).map(_._1)
}
def genContractInfos(outcomes: Vector[String], totalInput: CurrencyUnit): (
SingleNonceContractInfo,
SingleNonceContractInfo) = {
def genContractDescriptors(
outcomes: Vector[String],
totalInput: CurrencyUnit): (
EnumContractDescriptor,
EnumContractDescriptor) = {
val outcomeMap =
outcomes
.map(EnumOutcome.apply)
.zip(DLCTestUtil.genValues(outcomes.length, totalInput))
val info = SingleNonceContractInfo(outcomeMap)
val info = EnumContractDescriptor(outcomeMap)
val remoteInfo = info.flip(totalInput.satoshis)
(info, remoteInfo)
@ -60,8 +62,10 @@ object DLCTestUtil {
numDigits: Int,
totalCollateral: CurrencyUnit,
roundingIntervals: RoundingIntervals = RoundingIntervals.noRounding,
numRounds: Int = 0): (MultiNonceContractInfo, MultiNonceContractInfo) = {
val overMaxValue = Math.pow(10, numDigits).toLong
numRounds: Int = 0): (
NumericContractDescriptor,
NumericContractDescriptor) = {
val overMaxValue = Math.pow(2, numDigits).toLong
// Left collar goes from [0, botCollar]
val botCollar = NumberUtil.randomLong(overMaxValue / 2)
val halfWindow = scala.math.min(overMaxValue / 4, 2500)
@ -92,11 +96,8 @@ object DLCTestUtil {
}
RoundingIntervals(intervalStarts)
} else roundingIntervals
val info = MultiNonceContractInfo(func,
base = 10,
numDigits,
totalCollateral.satoshis,
roundingIntervalsToUse)
val info =
NumericContractDescriptor(func, numDigits, roundingIntervalsToUse)
val remoteInfo = info.flip(totalCollateral.satoshis)
(info, remoteInfo)
}

View file

@ -110,9 +110,9 @@ case class DLCPartyParams(
def toOffer(params: DLCParams): DLCOffer = {
DLCOffer(
OracleAndContractInfo(
params.oracleInfo,
SingleNonceContractInfo(params.contractInfo.map(_.toMapEntry))),
ContractInfo(
EnumContractDescriptor(params.contractInfo.map(_.toMapEntry)),
params.oracleInfo),
DLCPublicKeys(fundingPrivKey.publicKey, payoutAddress),
collateral.satoshis,
fundingInputs,
@ -135,7 +135,7 @@ case class SerializedContractInfoEntry(
object SerializedContractInfoEntry {
def fromContractInfo(contractInfo: SingleNonceContractInfo): Vector[
def fromContractDescriptor(contractInfo: EnumContractDescriptor): Vector[
SerializedContractInfoEntry] = {
contractInfo.map {
case (EnumOutcome(str), amt) =>
@ -147,7 +147,7 @@ object SerializedContractInfoEntry {
}
case class DLCParams(
oracleInfo: OracleInfo,
oracleInfo: EnumSingleOracleInfo,
contractInfo: Vector[SerializedContractInfoEntry],
contractMaturityBound: BlockTimeStamp,
contractTimeout: BlockTimeStamp,
@ -226,18 +226,8 @@ object SuccessTestVector extends TestVectorParser[SuccessTestVector] {
{ element => JsString(element.hex) }
)
implicit val oracleInfoFormat: Format[OracleInfo] = Format[OracleInfo](
{
_.validate[Map[String, String]]
.map(map =>
SingleNonceOracleInfo(SchnorrPublicKey(map("publicKey")),
SchnorrNonce(map("nonce"))))
},
{ info =>
Json.toJson(
Map("publicKey" -> info.pubKey.hex, "nonce" -> info.nonces.head.hex))
}
)
implicit val oracleInfoFormat: Format[EnumSingleOracleInfo] = hexFormat(
EnumSingleOracleInfo)
implicit val blockTimeStampFormat: Format[BlockTimeStamp] =
Format[BlockTimeStamp](

View file

@ -4,11 +4,11 @@ import org.bitcoins.core.currency.{CurrencyUnit, Satoshis}
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.dlc.DLCMessage.{
DLCSign,
SingleNonceContractInfo,
SingleNonceOracleInfo
EnumContractDescriptor,
EnumSingleOracleInfo
}
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.tlv.EnumOutcome
import org.bitcoins.core.protocol.tlv.{EnumOutcome, OracleAnnouncementV0TLV}
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.protocol.{BitcoinAddress, BlockTimeStamp}
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
@ -22,16 +22,19 @@ object DLCTxGen {
import DLCTLVGen._
def dlcParams(
contractInfo: SingleNonceContractInfo = genContractInfo(),
contractDescriptor: EnumContractDescriptor = genContractDescriptor(),
contractMaturityBound: BlockTimeStamp = BlockTimeStamp(100),
contractTimeout: BlockTimeStamp = BlockTimeStamp(200),
feeRate: SatoshisPerVirtualByte =
SatoshisPerVirtualByte(Satoshis(5))): DLCParams = {
val privKey = ECPrivateKey.freshPrivateKey
val kVal = ECPrivateKey.freshPrivateKey
val oracleInfo =
SingleNonceOracleInfo(privKey.schnorrPublicKey, kVal.schnorrNonce)
val realOutcome = contractInfo.keys(contractInfo.size / 2)
val oracleInfo = EnumSingleOracleInfo(
OracleAnnouncementV0TLV
.dummyForEventsAndKeys(privKey,
kVal.schnorrNonce,
contractDescriptor.keys))
val realOutcome = contractDescriptor.keys(contractDescriptor.size / 2)
val sig =
privKey.schnorrSignWithNonce(CryptoUtil
.sha256DLCAttestation(realOutcome.outcome)
@ -39,7 +42,7 @@ object DLCTxGen {
kVal)
DLCParams(
oracleInfo,
SerializedContractInfoEntry.fromContractInfo(contractInfo),
SerializedContractInfoEntry.fromContractDescriptor(contractDescriptor),
contractMaturityBound,
contractTimeout,
feeRate,
@ -141,10 +144,10 @@ object DLCTxGen {
acceptInputs: Vector[FundingInputTx],
numOutcomes: Int = 3): ValidTestInputs = {
val outcomes = DLCTestUtil.genOutcomes(numOutcomes)
val contractInfo = genContractInfo(outcomes)
val contractDescriptor = genContractDescriptor(outcomes)
validTestInputs(
params = dlcParams(contractInfo = contractInfo),
params = dlcParams(contractDescriptor = contractDescriptor),
offerParams = dlcPartyParams(fundingInputTxs = offerInputs),
acceptParams = dlcPartyParams(fundingInputTxs = acceptInputs)
)
@ -205,9 +208,10 @@ object DLCTxGen {
def randomTxTestVector(numOutcomes: Int)(implicit
ec: ExecutionContext): Future[DLCTxTestVector] = {
val outcomes = DLCTestUtil.genOutcomes(numOutcomes)
val contractInfo = genContractInfo(outcomes)
val contractDescriptor = genContractDescriptor(outcomes)
dlcTxTestVector(validTestInputs(dlcParams(contractInfo = contractInfo)))
dlcTxTestVector(
validTestInputs(dlcParams(contractDescriptor = contractDescriptor)))
}
def successTestVector(inputs: ValidTestInputs = validTestInputs())(implicit
@ -272,8 +276,9 @@ object DLCTxGen {
def randomSuccessTestVector(numOutcomes: Int)(implicit
ec: ExecutionContext): Future[SuccessTestVector] = {
val outcomes = DLCTestUtil.genOutcomes(numOutcomes)
val contractInfo = genContractInfo(outcomes)
val contractDescriptor = genContractDescriptor(outcomes)
successTestVector(validTestInputs(dlcParams(contractInfo = contractInfo)))
successTestVector(
validTestInputs(dlcParams(contractDescriptor = contractDescriptor)))
}
}

View file

@ -2,13 +2,9 @@ package org.bitcoins.dlc.testgen
import org.bitcoins.core.config.{BitcoinNetwork, RegTest}
import org.bitcoins.core.currency.CurrencyUnit
import org.bitcoins.core.protocol.dlc.DLCMessage.{
ContractInfo,
DLCAccept,
OracleInfo
}
import org.bitcoins.core.protocol.dlc._
import org.bitcoins.core.protocol.BitcoinAddress
import org.bitcoins.core.protocol.dlc.DLCMessage.{ContractInfo, DLCAccept}
import org.bitcoins.core.protocol.dlc._
import org.bitcoins.core.protocol.script.ScriptPubKey
import org.bitcoins.core.protocol.tlv.DLCOutcomeType
import org.bitcoins.core.protocol.transaction.Transaction
@ -58,7 +54,7 @@ case class TestDLCClient(
private val dlcExecutor = DLCExecutor(dlcTxSigner)
val messages: Vector[DLCOutcomeType] = offer.oracleAndContractInfo.allOutcomes
val messages: Vector[DLCOutcomeType] = offer.contractInfo.allOutcomes
val timeouts: DLCTimeouts = offer.timeouts
@ -127,7 +123,6 @@ object TestDLCClient {
def apply(
outcomes: ContractInfo,
oracleInfo: OracleInfo,
isInitiator: Boolean,
fundingPrivKey: ECPrivateKey,
payoutPrivKey: ECPrivateKey,
@ -147,8 +142,10 @@ object TestDLCClient {
network
)
val remoteOutcomes: ContractInfo =
outcomes.flip((input + remoteInput).satoshis)
val remoteOutcomes: ContractInfo = {
outcomes.copy(contractDescriptor =
outcomes.contractDescriptor.flip((input + remoteInput).satoshis))
}
val changeAddress = BitcoinAddress.fromScriptPubKey(changeSPK, network)
val remoteChangeAddress =
@ -185,8 +182,7 @@ object TestDLCClient {
}
val offer = DLCMessage.DLCOffer(
oracleAndContractInfo =
DLCMessage.OracleAndContractInfo(oracleInfo, offerOutcomes),
contractInfo = offerOutcomes,
pubKeys = offerPubKeys,
totalCollateral = offerInput.satoshis,
fundingInputs = offerFundingInputs,

View file

@ -1,37 +1,36 @@
[ {
"tpeName" : "contract_info_v0",
"input" : "fda710550313383739383330313036323334303937393934330000000000000000133536333934333434353837303033323734353000000000074a9371132d373938323838383237313838393935333530000000000bebc200",
"tpeName" : "contract_descriptor_v0",
"input" : "fda71057031337343730333734383832303531313332393232000000000bebc200142d33363133323438373939393532393639383735000000000aeeb364142d343236333233333538353533323934303835370000000000000000",
"fields" : {
"tpe" : "fda710",
"length" : "55",
"length" : "57",
"outcomes" : [ {
"outcome" : "f8c323888565496f382949ff2d19303bb1e555f41faaccb31fd4592d1026a0bb",
"localPayout" : "0000000000000000"
}, {
"outcome" : "a368cb1585fec2782673feb9043fdaebd5428783a102cfde3a3886e86659172e",
"localPayout" : "00000000074a9371"
}, {
"outcome" : "680d392bea45c4bed262f3b180aa325da3a7b3d65de2f334c3c778761685cadc",
"outcome" : "18aadcad027d04c91806b55b6d533af4427f2196197463f9a71ca728219993b4",
"localPayout" : "000000000bebc200"
}, {
"outcome" : "241b50567287d20db68a351b6f06f4feda0d731d9744e2cea42076ca14a7f8fe",
"localPayout" : "000000000aeeb364"
}, {
"outcome" : "8fef4af31be25553203dafd671068dce1f8da1411c53a868a084941036c9c858",
"localPayout" : "0000000000000000"
} ]
}
}, {
"tpeName" : "oracle_info_v0",
"input" : "fda7124081643fa4a105ee583e26f77db925c81e89781f3e71cf28e3b517a5e984a093d6218f01ddec118e1e2a5ee1a0ac8d8618f59eaaed3e83d33ae3ea07752c76af7c",
"input" : "fda712a8fdd824a4fab22628f6e2602e1671c286a2f63a9246794008627a1749639217f4214cb4a9494c93d1a852221080f44f697adb4355df59eb339f6ba0f9b01ba661a8b108d4da078bbb1d34e7729e38e2ae34236e776da121af442626fa31e31ae55a279a0bfdd8224000013cfba011378411b20a5ab773cb95daab93e9bcd1e4cce44986a7dda84e01841b00000000fdd8061000020664756d6d79310664756d6d79320564756d6d79",
"fields" : {
"tpe" : "fda712",
"length" : "40",
"pubKey" : "81643fa4a105ee583e26f77db925c81e89781f3e71cf28e3b517a5e984a093d6",
"rValue" : "218f01ddec118e1e2a5ee1a0ac8d8618f59eaaed3e83d33ae3ea07752c76af7c"
"length" : "a8",
"announcement" : "fdd824a4fab22628f6e2602e1671c286a2f63a9246794008627a1749639217f4214cb4a9494c93d1a852221080f44f697adb4355df59eb339f6ba0f9b01ba661a8b108d4da078bbb1d34e7729e38e2ae34236e776da121af442626fa31e31ae55a279a0bfdd8224000013cfba011378411b20a5ab773cb95daab93e9bcd1e4cce44986a7dda84e01841b00000000fdd8061000020664756d6d79310664756d6d79320564756d6d79"
}
}, {
"tpeName" : "funding_input_v0",
"input" : "fda71437002902000000000100c2eb0b00000000160014053d0b5c0436a6d1718648f13b85dd86fde7d0d40000000000000000ffffffff006b0000",
"input" : "fda71437002902000000000100c2eb0b00000000160014e70dcc9ffa7ff84c889c9e79b218708bae3bc9580000000000000000ffffffff006b0000",
"fields" : {
"tpe" : "fda714",
"length" : "37",
"prevTxLen" : "0029",
"prevTx" : "02000000000100c2eb0b00000000160014053d0b5c0436a6d1718648f13b85dd86fde7d0d400000000",
"prevTx" : "02000000000100c2eb0b00000000160014e70dcc9ffa7ff84c889c9e79b218708bae3bc95800000000",
"prevTxVout" : "00000000",
"sequence" : "ffffffff",
"maxWitnessLen" : "006b",
@ -40,24 +39,24 @@
}
}, {
"tpeName" : "cet_adaptor_signatures_v0",
"input" : "fda716fd01e703018c8ec8726fe693d4b6b6bffcca429101448a80096eafc35d7298ea42770a19aa16a8ef931403b93432d660033d9d00ceb5ca9c8d3d66c68e2d6f75b67e8e239c0059184e0b0bcff0568fe91e651e35353cad516c714016e29a2c1a4f4ddb7529b4142330861ef4fe794171617fbe3c386d26cf87fc616d9672fd627e035994ff6c5b190906464fd120ef63b9ec7a37082a1ad7593ec2eebd8621f10d1bc15c5b59005df60b967ec3b8201bec3c5754dbccc8ceb3031aac9a2fd341cbb72ab435227dc6e84d6d6638f4598edcf8dd99dd97154949309b236dd7bd570280434b9415aa01e145a633cc81286f8f1500025727152999586a26878d75e6b66a170a4ace3e86ac9bc5e6ee89dab9aef774561163258e9f058c7097d55b8a9b2506ecf1a8d1d611e7463500b98eb154dd37d54ffac093a947599483e6578db931b5b43c2de6e201dd29f198b2e154f9853987e9d31d48a272aa5afd9d774b02b55b27ce53d4136f2caed5168e29bab159809784c512c5066995d0ba511bbdc2c1ca8ed434f5be2d01c6e96bd0064385420b2db773738846811b29d0ba8dcb10bd73754e9ab2692ac853b313e0ee62b748082dabe8210956ed91efbd1d12c96ff8e33fc04b4e9108843bf6ed202ea81b556a310b45974d120cb502ebae132ffa3dc7e206cb1ff34205",
"input" : "fda716fd01e703005030c6b47800881605e2664820fe34507b5c27de3f21055494dadc6fc16ad26ef4009d6214467cd9bc75461b5bc401fb4f88f3ef6999788bb7c15abd2a6e6d0e00a8df0ec2fd10fa25fd1b3c783744526d9159563ac8050046c871e0517974c234d8b3804d87f952363d23a38d037a2350eeb854bca721ffeced60bd2f4d3c322a28e679a28c0e25c98d52ab0dedbbc103639edf32f922c9d60bb0a399a10fec2a00c1c9af6bac6f2e25ee91d918f82215a64452233362732d4b003204d3d4656f1b480d3274963308e7fe9b6d7bce87ceb92f1270bf954242d4a7b44c218093a741018d917c2e18d30840f8c0892192174459819699924c2f67b169b8a7ff44221760e0e69a6f3ee9bf531869fdec716b2863d48325d4aa41302eaed3b86b261d9380d77ae7cc5e1a9950b0b962d0f218802221b087f90e584a4d9b384fafa7a016c50025b86e63b3bf4edfba24bfdc019832ea1be085700aa8358ff5b5e510414915f5a204ad05b8866c9d83ba396b91c8ff5461c80c6f091d576afdbe98971d885ca700437301b19548a3ed227301acb3a8ec791c2ff52035007077caa3ca9d012860d5329024c06aa12b1154be4298a659a294a45b257f0d3c282d9d7adbe0db7972cd5e141e5bdb3b6db5460b583cbc38a805a63cb1352b489000d2ec4cc43dc40e77",
"fields" : {
"tpe" : "fda716",
"length" : "fd01e7",
"sigs" : [ {
"encryptedSig" : "018c8ec8726fe693d4b6b6bffcca429101448a80096eafc35d7298ea42770a19aa16a8ef931403b93432d660033d9d00ceb5ca9c8d3d66c68e2d6f75b67e8e239c",
"dleqProof" : "0059184e0b0bcff0568fe91e651e35353cad516c714016e29a2c1a4f4ddb7529b4142330861ef4fe794171617fbe3c386d26cf87fc616d9672fd627e035994ff6c5b190906464fd120ef63b9ec7a37082a1ad7593ec2eebd8621f10d1bc15c5b59"
"encryptedSig" : "005030c6b47800881605e2664820fe34507b5c27de3f21055494dadc6fc16ad26ef4009d6214467cd9bc75461b5bc401fb4f88f3ef6999788bb7c15abd2a6e6d0e",
"dleqProof" : "00a8df0ec2fd10fa25fd1b3c783744526d9159563ac8050046c871e0517974c234d8b3804d87f952363d23a38d037a2350eeb854bca721ffeced60bd2f4d3c322a28e679a28c0e25c98d52ab0dedbbc103639edf32f922c9d60bb0a399a10fec2a"
}, {
"encryptedSig" : "005df60b967ec3b8201bec3c5754dbccc8ceb3031aac9a2fd341cbb72ab435227dc6e84d6d6638f4598edcf8dd99dd97154949309b236dd7bd570280434b9415aa",
"dleqProof" : "01e145a633cc81286f8f1500025727152999586a26878d75e6b66a170a4ace3e86ac9bc5e6ee89dab9aef774561163258e9f058c7097d55b8a9b2506ecf1a8d1d611e7463500b98eb154dd37d54ffac093a947599483e6578db931b5b43c2de6e2"
"encryptedSig" : "00c1c9af6bac6f2e25ee91d918f82215a64452233362732d4b003204d3d4656f1b480d3274963308e7fe9b6d7bce87ceb92f1270bf954242d4a7b44c218093a741",
"dleqProof" : "018d917c2e18d30840f8c0892192174459819699924c2f67b169b8a7ff44221760e0e69a6f3ee9bf531869fdec716b2863d48325d4aa41302eaed3b86b261d9380d77ae7cc5e1a9950b0b962d0f218802221b087f90e584a4d9b384fafa7a016c5"
}, {
"encryptedSig" : "01dd29f198b2e154f9853987e9d31d48a272aa5afd9d774b02b55b27ce53d4136f2caed5168e29bab159809784c512c5066995d0ba511bbdc2c1ca8ed434f5be2d",
"dleqProof" : "01c6e96bd0064385420b2db773738846811b29d0ba8dcb10bd73754e9ab2692ac853b313e0ee62b748082dabe8210956ed91efbd1d12c96ff8e33fc04b4e9108843bf6ed202ea81b556a310b45974d120cb502ebae132ffa3dc7e206cb1ff34205"
"encryptedSig" : "0025b86e63b3bf4edfba24bfdc019832ea1be085700aa8358ff5b5e510414915f5a204ad05b8866c9d83ba396b91c8ff5461c80c6f091d576afdbe98971d885ca7",
"dleqProof" : "00437301b19548a3ed227301acb3a8ec791c2ff52035007077caa3ca9d012860d5329024c06aa12b1154be4298a659a294a45b257f0d3c282d9d7adbe0db7972cd5e141e5bdb3b6db5460b583cbc38a805a63cb1352b489000d2ec4cc43dc40e77"
} ]
}
}, {
"tpeName" : "funding_signatures_v0",
"input" : "fda71871000100020048304502210090276d8795ed508925d627e231a9652f9b6eac74bebc84c280eb10456e16acde02206f37585646dc65454f33e1dc7cf08fa7557e40d2318314c93579992e081f1923010021021db97135d3ab89e799b2b8efec086038041f439034fc3f0443ae986fe02bd552",
"input" : "fda718710001000200483045022100b16f3aad7591ab2d22e33c84c9a385546776810cf9134f4b38ef714635cb312a02202e3c1a7b983501de70f4316ebce9e484ce87f4bdf067eba50597503b3e219ca601002102b8c26d692ae3f6f3dcb78c5bb660b90bdc90e7cdaafeec94f63de0fa08ab403e",
"fields" : {
"tpe" : "fda718",
"length" : "71",
@ -66,60 +65,59 @@
"stackLen" : "0002",
"stack" : [ {
"stackElementLen" : "0048",
"stackElement" : "304502210090276d8795ed508925d627e231a9652f9b6eac74bebc84c280eb10456e16acde02206f37585646dc65454f33e1dc7cf08fa7557e40d2318314c93579992e081f192301"
"stackElement" : "3045022100b16f3aad7591ab2d22e33c84c9a385546776810cf9134f4b38ef714635cb312a02202e3c1a7b983501de70f4316ebce9e484ce87f4bdf067eba50597503b3e219ca601"
}, {
"stackElementLen" : "0021",
"stackElement" : "021db97135d3ab89e799b2b8efec086038041f439034fc3f0443ae986fe02bd552"
"stackElement" : "02b8c26d692ae3f6f3dcb78c5bb660b90bdc90e7cdaafeec94f63de0fa08ab403e"
} ]
} ]
}
}, {
"tpeName" : "offer_dlc_v0",
"input" : "a71a0006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910ffda7105703133736363931393336343532323830393236343900000000097a46cd142d34343932383232333839373539363634313830000000000bebc200142d343930313436363239333731383033323434310000000000000000fda71240f17f614d83f0a12f70c99aac831a33d1374c3cd1d435f346215dcb3924cd3c754b75d5510c30f713c39fa1b42198f8de22042aa38f84aff186f5fe83abc98299030407874e02b13492a679915f678d61a063f5dee609135e8489caf0aa85ab54ef00160014af7efec124314243c07b2aff5feb98762e408e0c0000000005f5e1000001fda71437002902000000000100c2eb0b00000000160014fe759e578312948a553a8a5dbe5bb588f1c4f1890000000000000000ffffffff006b00000016001494eb035c0ef04a32edee53d9810a9e663f8642b5000000000000000100000064000000c8",
"input" : "a71a0006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910ffdd82efd0139000000000bebc200fda71054031331313735353432353737323939343932373434000000000592ab711331393738383935313131323435383436303133000000000000000012353538303833303332333138373338333634000000000bebc200fda712d5fdd824d1c16171569e48d1dcaa9144ee9021558a979792eb5670a581b362d788ff5bed31a97328c9bc55d2eac086f271d194ba106f0bbc8061cc340971be61a7fdb35737015f4361a2eadd269728a4e107aa3b0de033951f849cde3cc0e0536c7bdedcf4fdd8226d00014c8d56e0967ad13461bab6ade6980179e7acde2c8deb1d35cb9fd523980434eb00000000fdd8063d000313313137353534323537373239393439323734341331393738383935313131323435383436303133123535383038333033323331383733383336340564756d6d790327efea09ff4dfb13230e887cbab8821d5cc249c7ff28668c6633ff9f4b4c08e3001600142bbdec425007dc360523b0294d2c64d2213af4980000000005f5e1000001fda71437002902000000000100c2eb0b00000000160014369d63a82ed846f4d47ad55045e594ab95539d600000000000000000ffffffff006b000000160014afa16f949f3055f38bd3a73312bed00b61558884000000000000000100000064000000c8",
"fields" : {
"tpe" : "a71a",
"contractFlags" : "00",
"chainHash" : "06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f",
"contractInfo" : "fda7105703133736363931393336343532323830393236343900000000097a46cd142d34343932383232333839373539363634313830000000000bebc200142d343930313436363239333731383033323434310000000000000000",
"oracleInfo" : "fda71240f17f614d83f0a12f70c99aac831a33d1374c3cd1d435f346215dcb3924cd3c754b75d5510c30f713c39fa1b42198f8de22042aa38f84aff186f5fe83abc98299",
"fundingPubKey" : "030407874e02b13492a679915f678d61a063f5dee609135e8489caf0aa85ab54ef",
"contractInfo" : "fdd82efd0139000000000bebc200fda71054031331313735353432353737323939343932373434000000000592ab711331393738383935313131323435383436303133000000000000000012353538303833303332333138373338333634000000000bebc200fda712d5fdd824d1c16171569e48d1dcaa9144ee9021558a979792eb5670a581b362d788ff5bed31a97328c9bc55d2eac086f271d194ba106f0bbc8061cc340971be61a7fdb35737015f4361a2eadd269728a4e107aa3b0de033951f849cde3cc0e0536c7bdedcf4fdd8226d00014c8d56e0967ad13461bab6ade6980179e7acde2c8deb1d35cb9fd523980434eb00000000fdd8063d000313313137353534323537373239393439323734341331393738383935313131323435383436303133123535383038333033323331383733383336340564756d6d79",
"fundingPubKey" : "0327efea09ff4dfb13230e887cbab8821d5cc249c7ff28668c6633ff9f4b4c08e3",
"payoutSPKLen" : "0016",
"payoutSPK" : "0014af7efec124314243c07b2aff5feb98762e408e0c",
"payoutSPK" : "00142bbdec425007dc360523b0294d2c64d2213af498",
"totalCollateralSatoshis" : "0000000005f5e100",
"fundingInputsLen" : "0001",
"fundingInputs" : [ "fda71437002902000000000100c2eb0b00000000160014fe759e578312948a553a8a5dbe5bb588f1c4f1890000000000000000ffffffff006b0000" ],
"fundingInputs" : [ "fda71437002902000000000100c2eb0b00000000160014369d63a82ed846f4d47ad55045e594ab95539d600000000000000000ffffffff006b0000" ],
"changeSPKLen" : "0016",
"changeSPK" : "001494eb035c0ef04a32edee53d9810a9e663f8642b5",
"changeSPK" : "0014afa16f949f3055f38bd3a73312bed00b61558884",
"feeRate" : "0000000000000001",
"contractMaturityBound" : "00000064",
"contractTimeout" : "000000c8"
}
}, {
"tpeName" : "accept_dlc_v0",
"input" : "a71c9d322b27cfbbb5c1387acd985b554953eafbce8db21d0cf244750b9306ef561d0000000005f5e10002a997405cbaa2c40ae72e51e834074d29f3c825921543f59f8d7152ac5640f41000160014dba4cf029c1dd41a0905a039c7559c8a07712fee0001fda71437002902000000000100c2eb0b00000000160014c97292d1dfb59da5ae6a9da2c0102d07e53bba080000000000000000ffffffff006b000000160014c50e3cb3a242d25050ab04128acd2d8ee3e2eecffda716fd01e703018b97dd8ca3771815391051c0fd9ece7b3cba3b2e88896a5aaf2e8f61c3dc959ac3f5e712d15939a090ba9a1f626894863ef34162c392c6db53d3d5de76ff7907012544a0d021ec9b47ee3fabe5d3107ecfbe7d2b7959aabf5c7cd26526b5b0ac576993cd15bb21ab6786b18bcfa5206a97871f9d051e5e949f18b3fe489db1327b52acf8f1f1bc3c001a6c096e5cf8d1cca2eda845f56a16be241d03bb72fb81290131f2ef57d89dea2eda03d08b402c66c79333caa6a3139c16ac1a771ddc56ae7ff75f34eaa04d1fc8661466589cb87d22633b22db1431052d7f28679a2d9c6d8d01128ce8235b9045a8a70e5987228a18e722847d8d85756a49ad574812b84d35c6766845a99166da49d3e31275bc8bb5a5f6315710269ca4670fad828bdf01c5a1e9328f29381138a1fac1a5189b1d0c9ad24c200b8d87e8a0409eaa8c9ab7981d00251f9ec2d54f67edbf6cced161827da759425cc3e256eca68c7070b80484a23cbecbdf0c0053cc24d070739cc25b4987759e69d6f66be179eed83bd5a052649400fcbff364d8de8fe6d7ac3a672f2824542a8f0386f1e94a4f7f5f050c5bc7021dcada2ae29a175903a51457bec01bc33d6489df4fb7e2f4dd620de945cc00dab84d6ba82e0af199ae93aba23382dc2772bf0d8e8be3516ac7045a57ec5ce009cb071c251c7275706eca59f1a03e0094a120b0bbdc377b5464d049d63fc468aa7b4c5a372dd656334293abd0424b48acd8569dabd4fc3bc563dc90c27ca8700c07fdd82600",
"input" : "a71c960fb5f7960382ac7e76f3e24eb6b00059b1e68632a946843c22e1f65fdf216a0000000005f5e100026d8bec9093f96ccc42de166cb9a6c576c95fc24ee16b10e87c3baaa4e49684d90016001436054fa379f7564b5e458371db643666365c8fb30001fda71437002902000000000100c2eb0b000000001600149ea3bf2d6eb9c2ffa35e36f41e117403ed7fafe90000000000000000ffffffff006b000000160014074c82dbe058212905bacc61814456b7415012edfda716fd01e703016292f1b5c67b675aea69c95ec81e8462ab5bb9b7a01f810f6d1a7d1d886893b3605fe7fcb75a14b1b1de917917d37e9efac6437d7a080da53fb6dbbcfbfbe7a801efbecb2bce89556e1fb4d31622628830e02a6d04c487f67aca20e9f60fb127f985293541cd14e2bf04e4777d50953531e169dd37c65eb3cc17d6b5e4dbe58487f9fae1f68f603fe014a699a346b14a63048c26c9b31236d83a7e369a2b29a29200e52fe05d832bcce4538d9c27f3537a0f2086b265b6498f30cf667f77ff2fa87606574bc9a915ef57f7546ebb6852a490ad0547bdc52b19791d2d0f0cc0acabab01f32459001a28850fa8ee4278111deb0494a8175f02e31a1c18b39bd82ec64026a6f341bcd5ba169d67b855030e36bdc65feecc0397a07d3bc514da69811ec5485f5553aebda782bc5ac9b47e8e11d701a38ef2c2b7d8af3906dd8dfc759754ce006f769592c744141a5ddface6e98f756a9df1bb75ad41508ea013bdfee133b396d85be51f870bf2e0ae836bfa984109dab96cc6f4ab2a7f118bc6b0b25a4c70d401c768c1d677c6ff0b7ea69fdf29aff1000794227db368dff16e838d1f44c4afe9e952ee63d603f7b14de13c1d73b363cc2b1740d0b688e73d8e71cddf40f8e7e912df413903779c4e5d6644c504c8609baec8fdcb90d6d341cf316748f5d7945f7c8ad6de287b62a1ed1d74ed9116a5158abc7f97376d201caa88e0f9daad68fcda4c271cc003512e768f403a57e5242bd1f6aa1750d7f3597598094a43b1c7bbfdd82600",
"fields" : {
"tpe" : "a71c",
"tempContractId" : "9d322b27cfbbb5c1387acd985b554953eafbce8db21d0cf244750b9306ef561d",
"tempContractId" : "960fb5f7960382ac7e76f3e24eb6b00059b1e68632a946843c22e1f65fdf216a",
"totalCollateralSatoshis" : "0000000005f5e100",
"fundingPubKey" : "02a997405cbaa2c40ae72e51e834074d29f3c825921543f59f8d7152ac5640f410",
"fundingPubKey" : "026d8bec9093f96ccc42de166cb9a6c576c95fc24ee16b10e87c3baaa4e49684d9",
"payoutSPKLen" : "0016",
"payoutSPK" : "0014dba4cf029c1dd41a0905a039c7559c8a07712fee",
"payoutSPK" : "001436054fa379f7564b5e458371db643666365c8fb3",
"fundingInputsLen" : "0001",
"fundingInputs" : [ "fda71437002902000000000100c2eb0b00000000160014c97292d1dfb59da5ae6a9da2c0102d07e53bba080000000000000000ffffffff006b0000" ],
"fundingInputs" : [ "fda71437002902000000000100c2eb0b000000001600149ea3bf2d6eb9c2ffa35e36f41e117403ed7fafe90000000000000000ffffffff006b0000" ],
"changeSPKLen" : "0016",
"changeSPK" : "0014c50e3cb3a242d25050ab04128acd2d8ee3e2eecf",
"cetSignatures" : "fda716fd01e703018b97dd8ca3771815391051c0fd9ece7b3cba3b2e88896a5aaf2e8f61c3dc959ac3f5e712d15939a090ba9a1f626894863ef34162c392c6db53d3d5de76ff7907012544a0d021ec9b47ee3fabe5d3107ecfbe7d2b7959aabf5c7cd26526b5b0ac576993cd15bb21ab6786b18bcfa5206a97871f9d051e5e949f18b3fe489db1327b52acf8f1f1bc3c001a6c096e5cf8d1cca2eda845f56a16be241d03bb72fb81290131f2ef57d89dea2eda03d08b402c66c79333caa6a3139c16ac1a771ddc56ae7ff75f34eaa04d1fc8661466589cb87d22633b22db1431052d7f28679a2d9c6d8d01128ce8235b9045a8a70e5987228a18e722847d8d85756a49ad574812b84d35c6766845a99166da49d3e31275bc8bb5a5f6315710269ca4670fad828bdf01c5a1e9328f29381138a1fac1a5189b1d0c9ad24c200b8d87e8a0409eaa8c9ab7981d00251f9ec2d54f67edbf6cced161827da759425cc3e256eca68c7070b80484a23cbecbdf0c0053cc24d070739cc25b4987759e69d6f66be179eed83bd5a052649400fcbff364d8de8fe6d7ac3a672f2824542a8f0386f1e94a4f7f5f050c5bc7021dcada2ae29a175903a51457bec01bc33d6489df4fb7e2f4dd620de945cc00dab84d6ba82e0af199ae93aba23382dc2772bf0d8e8be3516ac7045a57ec5ce009cb",
"refundSignature" : "071c251c7275706eca59f1a03e0094a120b0bbdc377b5464d049d63fc468aa7b4c5a372dd656334293abd0424b48acd8569dabd4fc3bc563dc90c27ca8700c07",
"changeSPK" : "0014074c82dbe058212905bacc61814456b7415012ed",
"cetSignatures" : "fda716fd01e703016292f1b5c67b675aea69c95ec81e8462ab5bb9b7a01f810f6d1a7d1d886893b3605fe7fcb75a14b1b1de917917d37e9efac6437d7a080da53fb6dbbcfbfbe7a801efbecb2bce89556e1fb4d31622628830e02a6d04c487f67aca20e9f60fb127f985293541cd14e2bf04e4777d50953531e169dd37c65eb3cc17d6b5e4dbe58487f9fae1f68f603fe014a699a346b14a63048c26c9b31236d83a7e369a2b29a29200e52fe05d832bcce4538d9c27f3537a0f2086b265b6498f30cf667f77ff2fa87606574bc9a915ef57f7546ebb6852a490ad0547bdc52b19791d2d0f0cc0acabab01f32459001a28850fa8ee4278111deb0494a8175f02e31a1c18b39bd82ec64026a6f341bcd5ba169d67b855030e36bdc65feecc0397a07d3bc514da69811ec5485f5553aebda782bc5ac9b47e8e11d701a38ef2c2b7d8af3906dd8dfc759754ce006f769592c744141a5ddface6e98f756a9df1bb75ad41508ea013bdfee133b396d85be51f870bf2e0ae836bfa984109dab96cc6f4ab2a7f118bc6b0b25a4c70d401c768c1d677c6ff0b7ea69fdf29aff1000794227db368dff16e838d1f44c4afe9e952ee63d603f7b14de13c1d73b363cc2b1740d0b688e73d8e71cddf40f8e7e912df413903779c4e5d6644c504c8609baec8fdcb90d6d341cf316748f5d7945f",
"refundSignature" : "7c8ad6de287b62a1ed1d74ed9116a5158abc7f97376d201caa88e0f9daad68fcda4c271cc003512e768f403a57e5242bd1f6aa1750d7f3597598094a43b1c7bb",
"negotiationFields" : "fdd82600"
}
}, {
"tpeName" : "sign_dlc_v0",
"input" : "a71e59e67278aa6bd346ecb2147d6f3763c75047ada2088a3a0ca766effc149fcff4fda716fd01e70301f08da14891887cc00cf865b96d085db2e874dd5a5693b4eecf8029c7ef69781535d77210b2e4e22f0ea1dc1a61cfc883e8cc4f885e8b65e9f8f127957cca240f013feffdc413863e5c0dbcf59216d444fdc473a78ad0eda3625a66be484d2184d5939e8f7ee812acb9ef9e205e147a3e3d839380ed97609b7033b81b99416f748043f0426510c6c090b1b5d07216c719bc41f2d78855507358c45497e3c8d6d6b60190edadc2b7d7c96d7c7777cf3cf622e156d5203604f2c8e506d46dd5e50639f7bd0dc4c7a87d66442d1ad394ee1db6b82078b79b3fc1fa2f379763be45363aec01d2ad2e223aaf85978ab5880593e0c1e5405185b51656ac3fbf3a8cd1c7b2ae8d9e9aa04d1b05e473ba49dc5be919cfae63e96b843969612d4e9298aad57df99c5650930df7869a260884d33ea280a432f47d1ce976a3a8c1f6c5f6223a40c95601f06172c4977a2040c9c043b6757dfc143d8b1c94414db14cbe6d0112a15af8641b26c55e031d86fd1ccadef6c87bb8c1366ef7fac66931aecc086a6fce227eeb00741b84a32f49aacaf1600015aae3ae879439d71d81c69e19956d48b4794334a38534dd702a49edbc1113466e48b3c46d487bc035c1097376dbb05ea09889b2f77604605934bb6c776b2b719311bc6a8c06f6180002fbcf2d814f24e9f95963bdf4fa3e6b711593a9f56b9897a41c6d5debb9a00075e9d06c0b17116b69844ba129b861daf7919e86e366bdee5632164293a7a19131c6f1fe10fe6eedc7dcb36ffda718710001000200483045022100a5ff7bf98bd38d7b60e92c9a65083c3dbc102a7dd6c7cf3b090c2ab541fe3b2b02207f1f954d9c2336444171ab9508fa32e4608bf9a246452e53f6d793bca4d34dd6010021028a43479523323c8449fcb574b975af240a3a8d419b8c8246d729d4205c11d473",
"input" : "a71ec1c79e1e9e2fa2840b2514902ea244f39eb3001a4037a52ea43c797d4f841269fda716fd01e70300c706fe7ed70197a77397fb7ce8445fcf1d0b239b4ab41ebdad4f76e0a671d7830470f4fef96d0838e8f3cec33176a6a427d777b57d256f8545b570cd702972910192f8ad4eb341ac2867d203360516028b967b46ef0e5d1603b59a7d8ebc81d655dd11673febcf098006eba74b3604d0a1da818208ea2833079505a3dee7392255f0682e5b357a7382aae6e5bdcc728b94c9d0a52fb6f49ac5cbe32804fcfb71b10125e92381be588737f6ac5c28325c843c6551995880f830d926abd35ee3f8ed9fdfc47a5fd277d0df2a1f1d0bafba8efad7b127e2a232a4846ed90810c81e65750039dba803adb78100f20ca12b09b68a92b996b07a5ee47806379cedfa217848644f48d96ed6443ea7143adf1ce19a4386d0841b5071e31f5d3e4c479eab6a856b426c80d091da3de3959b29e4c2e3ae47ddba2758c2ca1c6a064dfee4671ba5010098f2595778a1596054ffcafb599f8f4a65c4215de757548c142d50b12eb67d4c1407690b808e33eba95fe818223886fd8e9ce4c758b4662636af663e0055376300a915ee71914ee8ae2c18d55b397649c0057a01f0a85c6ecf1b0eb26f7485f21b24c89013e1cb15a4bf40256e52a66751f33de46032db0801975933be2977a1e37d5d5f2d43f48481cc68783dbfeb21a35c62c1ca2eb6ee2ccfc12b74e9fd7a08fbf56fbb4bbcb01d1be3169dfda6f465020ee89c1e368d4a91e36d0d4cc44e6123db348c223988dfe147d611ae9351d6e78cfb902e3d01beed0c909e52a3aae9fda71870000100020047304402203812d7d194d44ec68f244cc3fd68507c563ec8c729fdfa3f4a79395b98abe84f0220704ab3f3ffd9c50c2488e59f90a90465fccc2d924d67a1e98a133676bf52f37201002102dde41aa1f21671a2e28ad92155d2d66e0b5428de15d18db4cbcf216bf00de919",
"fields" : {
"tpe" : "a71e",
"contractId" : "59e67278aa6bd346ecb2147d6f3763c75047ada2088a3a0ca766effc149fcff4",
"cetSignatures" : "fda716fd01e70301f08da14891887cc00cf865b96d085db2e874dd5a5693b4eecf8029c7ef69781535d77210b2e4e22f0ea1dc1a61cfc883e8cc4f885e8b65e9f8f127957cca240f013feffdc413863e5c0dbcf59216d444fdc473a78ad0eda3625a66be484d2184d5939e8f7ee812acb9ef9e205e147a3e3d839380ed97609b7033b81b99416f748043f0426510c6c090b1b5d07216c719bc41f2d78855507358c45497e3c8d6d6b60190edadc2b7d7c96d7c7777cf3cf622e156d5203604f2c8e506d46dd5e50639f7bd0dc4c7a87d66442d1ad394ee1db6b82078b79b3fc1fa2f379763be45363aec01d2ad2e223aaf85978ab5880593e0c1e5405185b51656ac3fbf3a8cd1c7b2ae8d9e9aa04d1b05e473ba49dc5be919cfae63e96b843969612d4e9298aad57df99c5650930df7869a260884d33ea280a432f47d1ce976a3a8c1f6c5f6223a40c95601f06172c4977a2040c9c043b6757dfc143d8b1c94414db14cbe6d0112a15af8641b26c55e031d86fd1ccadef6c87bb8c1366ef7fac66931aecc086a6fce227eeb00741b84a32f49aacaf1600015aae3ae879439d71d81c69e19956d48b4794334a38534dd702a49edbc1113466e48b3c46d487bc035c1097376dbb05ea09889b2f77604605934bb6c776b2b719311bc6a8c06f6180002fbcf2d814f24e9f95963bd",
"refundSignature" : "f4fa3e6b711593a9f56b9897a41c6d5debb9a00075e9d06c0b17116b69844ba129b861daf7919e86e366bdee5632164293a7a19131c6f1fe10fe6eedc7dcb36f",
"fundingSignatures" : "fda718710001000200483045022100a5ff7bf98bd38d7b60e92c9a65083c3dbc102a7dd6c7cf3b090c2ab541fe3b2b02207f1f954d9c2336444171ab9508fa32e4608bf9a246452e53f6d793bca4d34dd6010021028a43479523323c8449fcb574b975af240a3a8d419b8c8246d729d4205c11d473"
"contractId" : "c1c79e1e9e2fa2840b2514902ea244f39eb3001a4037a52ea43c797d4f841269",
"cetSignatures" : "fda716fd01e70300c706fe7ed70197a77397fb7ce8445fcf1d0b239b4ab41ebdad4f76e0a671d7830470f4fef96d0838e8f3cec33176a6a427d777b57d256f8545b570cd702972910192f8ad4eb341ac2867d203360516028b967b46ef0e5d1603b59a7d8ebc81d655dd11673febcf098006eba74b3604d0a1da818208ea2833079505a3dee7392255f0682e5b357a7382aae6e5bdcc728b94c9d0a52fb6f49ac5cbe32804fcfb71b10125e92381be588737f6ac5c28325c843c6551995880f830d926abd35ee3f8ed9fdfc47a5fd277d0df2a1f1d0bafba8efad7b127e2a232a4846ed90810c81e65750039dba803adb78100f20ca12b09b68a92b996b07a5ee47806379cedfa217848644f48d96ed6443ea7143adf1ce19a4386d0841b5071e31f5d3e4c479eab6a856b426c80d091da3de3959b29e4c2e3ae47ddba2758c2ca1c6a064dfee4671ba5010098f2595778a1596054ffcafb599f8f4a65c4215de757548c142d50b12eb67d4c1407690b808e33eba95fe818223886fd8e9ce4c758b4662636af663e0055376300a915ee71914ee8ae2c18d55b397649c0057a01f0a85c6ecf1b0eb26f7485f21b24c89013e1cb15a4bf40256e52a66751f33de46032db0801975933be2977a1e37d5d5f2d43f48481cc68783dbfeb21a35c62c1ca2eb6ee2ccfc12b74e9fd7a08",
"refundSignature" : "fbf56fbb4bbcb01d1be3169dfda6f465020ee89c1e368d4a91e36d0d4cc44e6123db348c223988dfe147d611ae9351d6e78cfb902e3d01beed0c909e52a3aae9",
"fundingSignatures" : "fda71870000100020047304402203812d7d194d44ec68f244cc3fd68507c563ec8c729fdfa3f4a79395b98abe84f0220704ab3f3ffd9c50c2488e59f90a90465fccc2d924d67a1e98a133676bf52f37201002102dde41aa1f21671a2e28ad92155d2d66e0b5428de15d18db4cbcf216bf00de919"
}
} ]

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -73,7 +73,7 @@ case class DLCSignatureVerifier(builder: DLCTxBuilder, isInitiator: Boolean)
builder.offerFundingKey
}
val adaptorPoint = builder.oracleAndContractInfo.sigPointForOutcome(outcome)
val adaptorPoint = builder.contractInfo.sigPointForOutcome(outcome)
val cet = Await.result(builder.buildCET(outcome), 5.seconds)
@ -93,7 +93,7 @@ case class DLCSignatureVerifier(builder: DLCTxBuilder, isInitiator: Boolean)
def verifyCETSigs(sigs: Vector[(DLCOutcomeType, ECAdaptorSignature)])(implicit
ec: ExecutionContext): Future[Boolean] = {
val correctNumberOfSigs =
sigs.size >= builder.oracleAndContractInfo.allOutcomes.length
sigs.size >= builder.contractInfo.allOutcomes.length
def runVerify(
outcomeSigs: Vector[(DLCOutcomeType, ECAdaptorSignature)]): Future[

View file

@ -115,7 +115,8 @@ trait TLVGen {
} yield OracleAnnouncementV0TLV(sig, pubkey, eventTLV)
}
def contractInfoV0TLV: Gen[ContractInfoV0TLV] = {
def contractDescriptorV0TLVWithTotalCollateral: Gen[
(ContractDescriptorV0TLV, Satoshis)] = {
for {
numOutcomes <- Gen.choose(2, 10)
outcomes <- Gen.listOfN(numOutcomes, StringGenerators.genString)
@ -123,18 +124,39 @@ trait TLVGen {
Gen
.choose(numOutcomes + 1, Long.MaxValue / 10000L)
.map(Satoshis.apply)
(contractInfo, _) =
DLCTestUtil.genContractInfos(outcomes.toVector, totalInput)
(contractDescriptor, _) =
DLCTestUtil.genContractDescriptors(outcomes.toVector, totalInput)
} yield {
contractInfo.toTLV
(contractDescriptor.toTLV, totalInput)
}
}
def contractDescriptorV0TLV: Gen[ContractDescriptorV0TLV] = {
contractDescriptorV0TLVWithTotalCollateral.map(_._1)
}
def oracleInfoV0TLV: Gen[OracleInfoV0TLV] = {
for {
pubKey <- CryptoGenerators.schnorrPublicKey
privKey <- CryptoGenerators.privateKey
rValue <- CryptoGenerators.schnorrNonce
} yield OracleInfoV0TLV(pubKey, rValue)
outcomes <- Gen.listOf(StringGenerators.genUTF8String)
} yield {
OracleInfoV0TLV(
OracleAnnouncementV0TLV.dummyForEventsAndKeys(
privKey,
rValue,
outcomes.toVector.map(EnumOutcome.apply)))
}
}
def contractInfoV0TLV: Gen[ContractInfoV0TLV] = {
for {
(descriptor, totalCollateral) <-
contractDescriptorV0TLVWithTotalCollateral
oracleInfo <- oracleInfoV0TLV
} yield {
ContractInfoV0TLV(totalCollateral, descriptor, oracleInfo)
}
}
def oracleInfoV0TLVWithKeys: Gen[
@ -142,8 +164,13 @@ trait TLVGen {
for {
privKey <- CryptoGenerators.privateKey
kValue <- CryptoGenerators.privateKey
outcomes <- Gen.listOf(StringGenerators.genUTF8String)
} yield {
(OracleInfoV0TLV(privKey.schnorrPublicKey, kValue.schnorrNonce),
(OracleInfoV0TLV(
OracleAnnouncementV0TLV.dummyForEventsAndKeys(
privKey,
kValue.schnorrNonce,
outcomes.toVector.map(EnumOutcome.apply))),
privKey,
kValue)
}
@ -235,7 +262,6 @@ trait TLVGen {
chainHash <- Gen.oneOf(
Networks.knownNetworks.map(_.chainParams.genesisBlock.blockHeader.hash))
contractInfo <- contractInfoV0TLV
oracleInfo <- oracleInfoV0TLV
fundingPubKey <- CryptoGenerators.publicKey
payoutAddress <- AddressGenerator.bitcoinAddress
totalCollateralSatoshis <- CurrencyUnitGenerator.positiveRealistic
@ -256,7 +282,6 @@ trait TLVGen {
0.toByte,
chainHash,
contractInfo,
oracleInfo,
fundingPubKey,
payoutAddress.scriptPubKey,
totalCollateralSatoshis,
@ -275,7 +300,10 @@ trait TLVGen {
offer <- dlcOfferTLV
(oracleInfo, oraclePrivKey, oracleRValue) <- oracleInfoV0TLVWithKeys
} yield {
(offer.copy(oracleInfo = oracleInfo), oraclePrivKey, oracleRValue)
(offer.copy(contractInfo =
offer.contractInfo.copy(oracleInfo = oracleInfo)),
oraclePrivKey,
oracleRValue)
}
}

View file

@ -1,7 +1,7 @@
package org.bitcoins.testkit.wallet
import org.bitcoins.core.currency.Satoshis
import org.bitcoins.core.protocol.dlc.DLCMessage.OracleAndContractInfo
import org.bitcoins.core.protocol.dlc.DLCMessage.ContractInfo
import org.bitcoins.db.AppConfig
import org.bitcoins.dlc.testgen.DLCTestUtil
import org.bitcoins.dlc.wallet.DLCAppConfig
@ -80,21 +80,22 @@ trait BitcoinSDualWalletTest extends BitcoinSWalletTest {
getBIP39PasswordOpt(),
Some(segwitWalletConf))(config2, system)
oracleAndContractInfo =
contractInfo =
if (multiNonce) {
DLCWalletUtil.multiNonceOracleAndContractInfo
DLCWalletUtil.multiNonceContractInfo
} else {
val numOutcomes = 8
val outcomes = DLCTestUtil.genOutcomes(numOutcomes)
val (contractInfo, _) =
DLCTestUtil.genContractInfos(outcomes, Satoshis(10000))
val (contractDescriptor, _) =
DLCTestUtil.genContractDescriptors(outcomes, Satoshis(10000))
OracleAndContractInfo(DLCWalletUtil.sampleOracleInfo,
contractInfo)
ContractInfo(Satoshis(10000),
contractDescriptor,
DLCWalletUtil.sampleOracleInfo)
}
(dlcWalletA, dlcWalletB) <-
DLCWalletUtil.initDLC(walletA, walletB, oracleAndContractInfo)
DLCWalletUtil.initDLC(walletA, walletB, contractInfo)
} yield (dlcWalletA, dlcWalletB),
destroy = { dlcWallets: (InitializedDLCWallet, InitializedDLCWallet) =>
for {

View file

@ -8,7 +8,11 @@ import org.bitcoins.core.policy.Policy
import org.bitcoins.core.protocol.dlc.DLCMessage._
import org.bitcoins.core.protocol.dlc._
import org.bitcoins.core.protocol.script.{P2WPKHWitnessSPKV0, P2WPKHWitnessV0}
import org.bitcoins.core.protocol.tlv.{DLCOutcomeType, EnumOutcome}
import org.bitcoins.core.protocol.tlv.{
DLCOutcomeType,
EnumOutcome,
OracleAnnouncementV0TLV
}
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.protocol.{BitcoinAddress, BlockTimeStamp}
import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
@ -44,15 +48,20 @@ object DLCWalletUtil {
lazy val loseHash: Sha256Digest =
CryptoUtil.sha256DLCAttestation(loseStr)
val sampleOutcomes: Vector[(EnumOutcome, Satoshis)] = Vector(
EnumOutcome(winStr) -> Satoshis(10000),
EnumOutcome(loseStr) -> Satoshis.zero)
lazy val sampleContractDescriptor: ContractDescriptor =
EnumContractDescriptor(sampleOutcomes)
lazy val sampleOracleInfo: OracleInfo =
SingleNonceOracleInfo(oraclePrivKey.schnorrPublicKey, rValue)
EnumSingleOracleInfo.dummyForKeys(oraclePrivKey,
rValue,
sampleOutcomes.map(_._1))
lazy val sampleContractInfo: ContractInfo =
SingleNonceContractInfo.fromStringVec(
Vector(winStr -> Satoshis(10000), loseStr -> Satoshis.zero))
lazy val sampleOracleAndContractInfo: OracleAndContractInfo =
OracleAndContractInfo(sampleOracleInfo, sampleContractInfo)
ContractInfo(Satoshis(10000), sampleContractDescriptor, sampleOracleInfo)
lazy val sampleOracleWinSig: SchnorrDigitalSignature =
oraclePrivKey.schnorrSignWithNonce(winHash.bytes, kValue)
@ -62,15 +71,18 @@ object DLCWalletUtil {
val numDigits: Int = 6
lazy val multiNonceContractInfo: MultiNonceContractInfo =
lazy val multiNonceContractDescriptor: NumericContractDescriptor =
DLCTestUtil.genMultiDigitContractInfo(numDigits, Satoshis(10000))._1
lazy val multiNonceOracleInfo: MultiNonceOracleInfo =
MultiNonceOracleInfo(oraclePrivKey.schnorrPublicKey,
rValues.take(numDigits))
lazy val multiNonceOracleInfo: NumericSingleOracleInfo =
NumericSingleOracleInfo(
OracleAnnouncementV0TLV.dummyForKeys(oraclePrivKey,
rValues.take(numDigits)))
lazy val multiNonceOracleAndContractInfo: OracleAndContractInfo =
OracleAndContractInfo(multiNonceOracleInfo, multiNonceContractInfo)
lazy val multiNonceContractInfo: ContractInfo =
ContractInfo(Satoshis(10000),
multiNonceContractDescriptor,
multiNonceOracleInfo)
lazy val dummyContractMaturity: BlockTimeStamp = BlockTimeStamp(1666335)
lazy val dummyContractTimeout: BlockTimeStamp = BlockTimeStamp(1666337)
@ -115,7 +127,7 @@ object DLCWalletUtil {
)
lazy val sampleDLCOffer: DLCOffer = DLCOffer(
sampleOracleAndContractInfo,
sampleContractInfo,
dummyDLCKeys,
Satoshis(5000),
Vector(dummyFundingInputs.head),
@ -125,12 +137,10 @@ object DLCWalletUtil {
)
lazy val sampleMultiNonceDLCOffer: DLCOffer =
sampleDLCOffer.copy(oracleAndContractInfo = multiNonceOracleAndContractInfo)
sampleDLCOffer.copy(contractInfo = multiNonceContractInfo)
lazy val sampleDLCParamHash: Sha256DigestBE =
DLCMessage.calcParamHash(sampleOracleInfo,
sampleContractInfo,
dummyTimeouts)
DLCMessage.calcParamHash(sampleContractInfo, dummyTimeouts)
lazy val dummyOutcomeSigs: Vector[(DLCOutcomeType, ECAdaptorSignature)] =
Vector(EnumOutcome(winStr) -> ECAdaptorSignature.dummy,
@ -174,16 +184,14 @@ object DLCWalletUtil {
def initDLC(
fundedWalletA: FundedDLCWallet,
fundedWalletB: FundedDLCWallet,
oracleAndContractInfo: OracleAndContractInfo)(implicit
ec: ExecutionContext): Future[
contractInfo: ContractInfo)(implicit ec: ExecutionContext): Future[
(InitializedDLCWallet, InitializedDLCWallet)] = {
val walletA = fundedWalletA.wallet
val walletB = fundedWalletB.wallet
for {
offer <- walletA.createDLCOffer(
oracleInfo = oracleAndContractInfo.oracleInfo,
contractInfo = oracleAndContractInfo.offerContractInfo,
contractInfo = contractInfo,
collateral = Satoshis(5000),
feeRateOpt = None,
locktime = dummyTimeouts.contractMaturity.toUInt32,