Decode DLC messages cli commands (#3636)

* Decode DLC messages cli commands

* Move decoding to picklers, add contractInfo decoder

* Add decoding examples
This commit is contained in:
benthecarman 2021-09-03 09:46:09 -05:00 committed by GitHub
parent 40f89af597
commit 515e85b1c5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 602 additions and 9 deletions

View file

@ -118,9 +118,6 @@ object Picklers {
implicit val partialSignaturePickler: ReadWriter[PartialSignature] = implicit val partialSignaturePickler: ReadWriter[PartialSignature] =
readwriter[String].bimap(_.hex, PartialSignature.fromHex) readwriter[String].bimap(_.hex, PartialSignature.fromHex)
implicit val dlcOfferTLVPickler: ReadWriter[DLCOfferTLV] =
readwriter[String].bimap(_.hex, DLCOfferTLV.fromHex)
implicit val lnMessageDLCOfferTLVPickler: ReadWriter[LnMessage[DLCOfferTLV]] = implicit val lnMessageDLCOfferTLVPickler: ReadWriter[LnMessage[DLCOfferTLV]] =
readwriter[String].bimap(_.hex, LnMessageFactory(DLCOfferTLV).fromHex) readwriter[String].bimap(_.hex, LnMessageFactory(DLCOfferTLV).fromHex)
@ -162,6 +159,213 @@ object Picklers {
LockUnspentOutputParameter] = LockUnspentOutputParameter] =
readwriter[Value].bimap(_.toJson, LockUnspentOutputParameter.fromJson) readwriter[Value].bimap(_.toJson, LockUnspentOutputParameter.fromJson)
// can't make implicit because it will overlap with ones needed for cli
val announcementV0JsonWriter: Writer[OracleAnnouncementV0TLV] =
writer[Obj].comap { announcement =>
val noncesJson = announcement.eventTLV.nonces.map { nonce =>
Str(nonce.hex)
}
val descriptorJson = announcement.eventTLV.eventDescriptor match {
case EnumEventDescriptorV0TLV(outcomes) =>
Obj("outcomes" -> outcomes.map(Str(_)))
case numeric: NumericEventDescriptorTLV =>
Obj(
"base" -> Num(numeric.base.toLong.toDouble),
"isSigned" -> Bool(numeric.isSigned),
"unit" -> Str(numeric.unit),
"precision" -> Num(numeric.precision.toLong.toDouble)
)
}
val maturityStr =
TimeUtil.iso8601ToString(Date.from(announcement.eventTLV.maturation))
val eventJson = Obj("nonces" -> noncesJson,
"maturity" -> Str(maturityStr),
"descriptor" -> descriptorJson,
"eventId" -> Str(announcement.eventTLV.eventId))
Obj(
"announcementSignature" -> Str(announcement.announcementSignature.hex),
"publicKey" -> Str(announcement.publicKey.hex),
"event" -> eventJson)
}
// can't make implicit because it will overlap with ones needed for cli
val oracleAnnouncementTLVJsonWriter: Writer[OracleAnnouncementTLV] =
writer[Value].comap { case v0: OracleAnnouncementV0TLV =>
writeJs(v0)(announcementV0JsonWriter)
}
// can't make implicit because it will overlap with ones needed for cli
val oracleAttestmentV0Writer: Writer[OracleAttestmentV0TLV] =
writer[Obj].comap { attestments =>
val sigsJson = attestments.sigs.map(sig => Str(sig.hex))
val valuesJson = attestments.outcomes.map(Str(_))
Obj("eventId" -> Str(attestments.eventId),
"signatures" -> sigsJson,
"values" -> valuesJson)
}
implicit val fundingInputV0Writer: Writer[FundingInputTLV] =
writer[Value].comap { case v0: FundingInputV0TLV =>
writeJs(v0)(fundingInputWriter)
}
implicit val fundingInputWriter: Writer[FundingInputV0TLV] =
writer[Obj].comap { input =>
import input._
val redeemScriptJson = redeemScriptOpt match {
case Some(rs) => Str(rs.hex)
case None => ujson.Null
}
Obj(
"inputSerialId" -> Num(inputSerialId.toBigInt.toDouble),
"prevTx" -> Str(prevTx.hex),
"prevTxVout" -> Num(prevTxVout.toLong.toDouble),
"sequence" -> Num(sequence.toLong.toDouble),
"maxWitnessLen" -> Num(maxWitnessLen.toLong.toDouble),
"redeemScript" -> redeemScriptJson
)
}
implicit val contractDescriptorV0Writer: Writer[ContractDescriptorV0TLV] =
writer[Obj].comap { v0 =>
import v0._
val outcomesJs = outcomes.map { case (outcome, payout) =>
Obj("outcome" -> Str(outcome),
"localPayout" -> Num(payout.toLong.toDouble))
}
Obj("outcomes" -> outcomesJs)
}
implicit val payoutFunctionV0TLVWriter: Writer[PayoutFunctionV0TLV] =
writer[Obj].comap { payoutFunc =>
import payoutFunc._
val pointsJs = points.map { point =>
Obj(
"outcome" -> Num(point.outcome.toDouble),
"payout" -> Num(point.value.toLong.toDouble),
"extraPrecision" -> Num(point.extraPrecision.toDouble),
"isEndpoint" -> Bool(point.isEndpoint)
)
}
Obj("points" -> pointsJs)
}
implicit val roundingIntervalsV0TLVWriter: Writer[RoundingIntervalsV0TLV] =
writer[Obj].comap { roundingIntervals =>
import roundingIntervals._
val intervalsJs = intervalStarts.map { i =>
Obj("beginInterval" -> Num(i._1.toDouble),
"roundingMod" -> Num(i._2.toLong.toDouble))
}
Obj("intervals" -> intervalsJs)
}
implicit val contractDescriptorV1Writer: Writer[ContractDescriptorV1TLV] =
writer[Obj].comap { v1 =>
import v1._
Obj("numDigits" -> Num(numDigits.toDouble),
"payoutFunction" -> writeJs(payoutFunction),
"roundingIntervals" -> writeJs(roundingIntervals))
}
implicit val contractDescriptorWriter: Writer[ContractDescriptorTLV] =
writer[Value].comap {
case v0: ContractDescriptorV0TLV =>
writeJs(v0)(contractDescriptorV0Writer)
case v1: ContractDescriptorV1TLV =>
writeJs(v1)(contractDescriptorV1Writer)
}
implicit val oracleInfoV0TLVWriter: Writer[OracleInfoV0TLV] =
writer[Obj].comap { oracleInfo =>
Obj(
"announcement" -> writeJs(oracleInfo.announcement)(
oracleAnnouncementTLVJsonWriter))
}
implicit val oracleInfoV1TLVWriter: Writer[OracleInfoV1TLV] =
writer[Obj].comap { oracleInfo =>
import oracleInfo._
Obj("threshold" -> Num(threshold.toDouble),
"announcements" -> oracles.map(o =>
writeJs(o)(oracleAnnouncementTLVJsonWriter)))
}
implicit val oracleParamsV0TLVWriter: Writer[OracleParamsV0TLV] =
writer[Obj].comap { params =>
import params._
Obj("maxErrorExp" -> Num(maxErrorExp.toDouble),
"minFailExp" -> Num(minFailExp.toDouble),
"maximizeCoverage" -> Bool(maximizeCoverage))
}
implicit val oracleParamsTLVWriter: Writer[OracleParamsTLV] =
writer[Value].comap { case v0: OracleParamsV0TLV =>
writeJs(v0)
}
implicit val oracleInfoV2TLVWriter: Writer[OracleInfoV2TLV] =
writer[Obj].comap { oracleInfo =>
import oracleInfo._
Obj("threshold" -> Num(threshold.toDouble),
"announcements" -> oracles.map(o =>
writeJs(o)(oracleAnnouncementTLVJsonWriter)),
"params" -> writeJs(params))
}
implicit val oracleInfoTLVWriter: Writer[OracleInfoTLV] =
writer[Value].comap {
case v0: OracleInfoV0TLV => writeJs(v0)
case v1: OracleInfoV1TLV => writeJs(v1)
case v2: OracleInfoV2TLV => writeJs(v2)
}
// can't make implicit because it will overlap with ones needed for cli
val contractInfoV0TLVJsonWriter: Writer[ContractInfoV0TLV] =
writer[Obj].comap { contractInfo =>
import contractInfo._
Obj("totalCollateral" -> Num(totalCollateral.toLong.toDouble),
"contractDescriptor" -> writeJs(contractDescriptor),
"oracleInfo" -> writeJs(oracleInfo))
}
implicit val offerTLVWriter: Writer[DLCOfferTLV] =
writer[Obj].comap { offer =>
import offer._
Obj(
"contractFlags" -> Str(contractFlags.toHexString),
"chainHash" -> Str(chainHash.hex),
"contractInfo" -> writeJs(contractInfo)(contractInfoV0TLVJsonWriter),
"fundingPubKey" -> Str(fundingPubKey.hex),
"payoutSPK" -> Str(payoutSPK.hex),
"payoutSerialId" -> Num(payoutSerialId.toBigInt.toDouble),
"offerCollateralSatoshis" -> Num(
totalCollateralSatoshis.toLong.toDouble),
"fundingInputs" -> fundingInputs.map(i => writeJs(i)),
"changeSPK" -> Str(changeSPK.hex),
"changeSerialId" -> Num(changeSerialId.toBigInt.toDouble),
"fundOutputSerialId" -> Num(fundOutputSerialId.toBigInt.toDouble),
"feeRatePerVb" -> Num(feeRate.toLong.toDouble),
"cetLocktime" -> Num(contractMaturityBound.toUInt32.toLong.toDouble),
"refundLocktime" -> Num(contractTimeout.toUInt32.toLong.toDouble)
)
}
implicit val offeredW: Writer[Offered] = implicit val offeredW: Writer[Offered] =
writer[Obj].comap { offered => writer[Obj].comap { offered =>
import offered._ import offered._
@ -614,9 +818,15 @@ object Picklers {
implicit val extPrivateKeyPickler: ReadWriter[ExtPrivateKey] = implicit val extPrivateKeyPickler: ReadWriter[ExtPrivateKey] =
readwriter[String].bimap(ExtKey.toString, ExtPrivateKey.fromString) readwriter[String].bimap(ExtKey.toString, ExtPrivateKey.fromString)
implicit val oracleAnnouncementTLV: ReadWriter[OracleAnnouncementV0TLV] =
readwriter[String].bimap(_.hex, OracleAnnouncementV0TLV.fromHex)
implicit val oracleAttestmentTLV: ReadWriter[OracleAttestmentTLV] = implicit val oracleAttestmentTLV: ReadWriter[OracleAttestmentTLV] =
readwriter[String].bimap(_.hex, OracleAttestmentTLV.fromHex) readwriter[String].bimap(_.hex, OracleAttestmentTLV.fromHex)
implicit val oracleAttestmentV0TLV: ReadWriter[OracleAttestmentV0TLV] =
readwriter[String].bimap(_.hex, OracleAttestmentV0TLV.fromHex)
implicit val ecPublicKeyPickler: ReadWriter[ECPublicKey] = implicit val ecPublicKeyPickler: ReadWriter[ECPublicKey] =
readwriter[String].bimap(_.hex, ECPublicKey.fromHex) readwriter[String].bimap(_.hex, ECPublicKey.fromHex)

View file

@ -196,6 +196,22 @@ object CliReaders {
val reads: String => ContractInfoV0TLV = ContractInfoV0TLV.fromHex val reads: String => ContractInfoV0TLV = ContractInfoV0TLV.fromHex
} }
implicit val announcementReads: Read[OracleAnnouncementV0TLV] =
new Read[OracleAnnouncementV0TLV] {
val arity: Int = 1
val reads: String => OracleAnnouncementV0TLV =
OracleAnnouncementV0TLV.fromHex
}
implicit val attestmentReads: Read[OracleAttestmentV0TLV] =
new Read[OracleAttestmentV0TLV] {
val arity: Int = 1
val reads: String => OracleAttestmentV0TLV =
OracleAttestmentV0TLV.fromHex
}
implicit val blockStampReads: Read[BlockStamp] = implicit val blockStampReads: Read[BlockStamp] =
new Read[BlockStamp] { new Read[BlockStamp] {
val arity: Int = 1 val arity: Int = 1

View file

@ -734,6 +734,58 @@ object ConsoleCli {
})) }))
), ),
note(sys.props("line.separator") + "=== DLC ==="), note(sys.props("line.separator") + "=== DLC ==="),
cmd("decodecontractinfo")
.action((_, conf) => conf.copy(command = DecodeContractInfo(null)))
.text("Decodes a contract info into json")
.children(
arg[ContractInfoV0TLV]("contractinfo")
.text("Hex encoded contract info")
.required()
.action((contractInfo, conf) =>
conf.copy(command = conf.command match {
case decode: DecodeContractInfo =>
decode.copy(contractInfo = contractInfo)
case other => other
}))),
cmd("decodeoffer")
.action((_, conf) => conf.copy(command = DecodeOffer(null)))
.text("Decodes an offer message into json")
.children(
arg[LnMessage[DLCOfferTLV]]("offer")
.text("Hex encoded dlc offer message")
.required()
.action((offer, conf) =>
conf.copy(command = conf.command match {
case decode: DecodeOffer =>
decode.copy(offer = offer)
case other => other
}))),
cmd("decodeannouncement")
.action((_, conf) => conf.copy(command = DecodeAnnouncement(null)))
.text("Decodes an oracle announcement message into json")
.children(
arg[OracleAnnouncementV0TLV]("announcement")
.text("Hex encoded oracle announcement message")
.required()
.action((ann, conf) =>
conf.copy(command = conf.command match {
case decode: DecodeAnnouncement =>
decode.copy(announcement = ann)
case other => other
}))),
cmd("decodeattestments")
.action((_, conf) => conf.copy(command = DecodeAttestments(null)))
.text("Decodes an oracle attestments message into json")
.children(
arg[OracleAttestmentV0TLV]("attestments")
.text("Hex encoded oracle attestments message")
.required()
.action((attestments, conf) =>
conf.copy(command = conf.command match {
case decode: DecodeAttestments =>
decode.copy(sigs = attestments)
case other => other
}))),
cmd("getdlchostaddress") cmd("getdlchostaddress")
.action((_, conf) => conf.copy(command = GetDLCHostAddress)) .action((_, conf) => conf.copy(command = GetDLCHostAddress))
.text("Returns the public listening address of the DLC Node"), .text("Returns the public listening address of the DLC Node"),
@ -1569,7 +1621,19 @@ object ConsoleCli {
RequestParam("walletinfo") RequestParam("walletinfo")
// DLCs // DLCs
case GetDLCHostAddress => RequestParam("getdlchostaddress") case GetDLCHostAddress => RequestParam("getdlchostaddress")
case GetDLCs => RequestParam("getdlcs")
case DecodeContractInfo(contractInfo) =>
RequestParam("decodecontractinfo", Seq(up.writeJs(contractInfo)))
case DecodeOffer(offer) =>
RequestParam("decodeoffer", Seq(up.writeJs(offer)))
case DecodeAnnouncement(announcement) =>
RequestParam("decodeannouncement", Seq(up.writeJs(announcement)))
case DecodeAttestments(attestments) =>
RequestParam("decodeattestments", Seq(up.writeJs(attestments)))
case GetDLCs => RequestParam("getdlcs")
case GetDLC(dlcId) => case GetDLC(dlcId) =>
RequestParam("getdlc", Seq(up.writeJs(dlcId))) RequestParam("getdlc", Seq(up.writeJs(dlcId)))
case CreateDLCOffer(contractInfo, case CreateDLCOffer(contractInfo,
@ -1954,6 +2018,18 @@ object CliCommand {
// DLC // DLC
case object GetDLCHostAddress extends AppServerCliCommand case object GetDLCHostAddress extends AppServerCliCommand
case class DecodeContractInfo(contractInfo: ContractInfoV0TLV)
extends AppServerCliCommand
case class DecodeOffer(offer: LnMessage[DLCOfferTLV])
extends AppServerCliCommand
case class DecodeAnnouncement(announcement: OracleAnnouncementV0TLV)
extends AppServerCliCommand
case class DecodeAttestments(sigs: OracleAttestmentV0TLV)
extends AppServerCliCommand
case class CreateDLCOffer( case class CreateDLCOffer(
contractInfo: ContractInfoV0TLV, contractInfo: ContractInfoV0TLV,
collateral: Satoshis, collateral: Satoshis,

View file

@ -4,6 +4,7 @@ import akka.actor.ActorSystem
import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server._ import akka.http.scaladsl.server._
import org.bitcoins.commons.jsonmodels.{SerializedPSBT, SerializedTransaction} import org.bitcoins.commons.jsonmodels.{SerializedPSBT, SerializedTransaction}
import org.bitcoins.commons.serializers.Picklers._
import org.bitcoins.core.hd.AddressType import org.bitcoins.core.hd.AddressType
import org.bitcoins.core.protocol.script.{ import org.bitcoins.core.protocol.script.{
MultiSignatureScriptPubKey, MultiSignatureScriptPubKey,
@ -15,6 +16,7 @@ import org.bitcoins.core.psbt.PSBT
import org.bitcoins.server.BitcoinSAppConfig.toChainConf import org.bitcoins.server.BitcoinSAppConfig.toChainConf
import org.bitcoins.server.routes.{Server, ServerCommand, ServerRoute} import org.bitcoins.server.routes.{Server, ServerCommand, ServerRoute}
import ujson._ import ujson._
import upickle.default._
import scala.collection.mutable import scala.collection.mutable
import scala.concurrent.Future import scala.concurrent.Future
@ -145,6 +147,40 @@ case class CoreRoutes()(implicit system: ActorSystem, config: BitcoinSAppConfig)
} }
} }
case ServerCommand("decodeoffer", arr) =>
withValidServerCommand(DecodeOffer.fromJsArr(arr)) {
case DecodeOffer(offerTLV) =>
complete {
Server.httpSuccess(writeJs(offerTLV))
}
}
case ServerCommand("decodecontractinfo", arr) =>
withValidServerCommand(DecodeContractInfo.fromJsArr(arr)) {
case DecodeContractInfo(contractInfo) =>
complete {
Server.httpSuccess(
writeJs(contractInfo)(contractInfoV0TLVJsonWriter))
}
}
case ServerCommand("decodeannouncement", arr) =>
withValidServerCommand(DecodeAnnouncement.fromJsArr(arr)) {
case DecodeAnnouncement(announcement) =>
complete {
Server.httpSuccess(
writeJs(announcement)(oracleAnnouncementTLVJsonWriter))
}
}
case ServerCommand("decodeattestments", arr) =>
withValidServerCommand(DecodeAttestations.fromJsArr(arr)) {
case DecodeAttestations(attestments) =>
complete {
Server.httpSuccess(writeJs(attestments)(oracleAttestmentV0Writer))
}
}
case ServerCommand("createmultisig", arr) => case ServerCommand("createmultisig", arr) =>
withValidServerCommand(CreateMultisig.fromJsArr(arr)) { withValidServerCommand(CreateMultisig.fromJsArr(arr)) {
case CreateMultisig(requiredKeys, keys, addressType) => case CreateMultisig(requiredKeys, keys, addressType) =>

View file

@ -21,7 +21,7 @@ import ujson._
import java.io.File import java.io.File
import java.net.{InetSocketAddress, URI} import java.net.{InetSocketAddress, URI}
import java.nio.file.Path import java.nio.file.Path
import scala.util.{Failure, Try} import scala.util._
case class GetNewAddress(labelOpt: Option[AddressLabelTag]) case class GetNewAddress(labelOpt: Option[AddressLabelTag])
@ -677,6 +677,102 @@ object CreateDLCOffer extends ServerJsonModels {
} }
} }
case class DecodeContractInfo(contractInfo: ContractInfoV0TLV)
object DecodeContractInfo extends ServerJsonModels {
def fromJsArr(jsArr: ujson.Arr): Try[DecodeContractInfo] = {
jsArr.arr.toList match {
case contractInfoJs :: Nil =>
Try {
val contractInfo = ContractInfoV0TLV(contractInfoJs.str)
DecodeContractInfo(contractInfo)
}
case Nil =>
Failure(new IllegalArgumentException("Missing contractInfo argument"))
case other =>
Failure(
new IllegalArgumentException(
s"Bad number of arguments: ${other.length}. Expected: 1"))
}
}
}
case class DecodeOffer(offer: DLCOfferTLV)
object DecodeOffer extends ServerJsonModels {
def fromJsArr(jsArr: ujson.Arr): Try[DecodeOffer] = {
jsArr.arr.toList match {
case offerJs :: Nil =>
Try {
val offer: LnMessage[DLCOfferTLV] =
LnMessageFactory(DLCOfferTLV).fromHex(offerJs.str)
DecodeOffer(offer.tlv)
} match {
case Success(value) => Success(value)
case Failure(_) =>
Try {
val offer = DLCOfferTLV.fromHex(offerJs.str)
DecodeOffer(offer)
}
}
case Nil =>
Failure(new IllegalArgumentException("Missing offer argument"))
case other =>
Failure(
new IllegalArgumentException(
s"Bad number of arguments: ${other.length}. Expected: 1"))
}
}
}
case class DecodeAnnouncement(announcement: OracleAnnouncementTLV)
object DecodeAnnouncement extends ServerJsonModels {
def fromJsArr(jsArr: ujson.Arr): Try[DecodeAnnouncement] = {
jsArr.arr.toList match {
case annJs :: Nil =>
Try {
val announcementTLV = OracleAnnouncementTLV(annJs.str)
DecodeAnnouncement(announcementTLV)
}
case Nil =>
Failure(new IllegalArgumentException("Missing announcement argument"))
case other =>
Failure(
new IllegalArgumentException(
s"Bad number of arguments: ${other.length}. Expected: 1"))
}
}
}
case class DecodeAttestations(announcement: OracleAttestmentV0TLV)
object DecodeAttestations extends ServerJsonModels {
def fromJsArr(jsArr: ujson.Arr): Try[DecodeAttestations] = {
jsArr.arr.toList match {
case attestmentJs :: Nil =>
Try {
val attestments = OracleAttestmentV0TLV(attestmentJs.str)
DecodeAttestations(attestments)
}
case Nil =>
Failure(new IllegalArgumentException("Missing attestment argument"))
case other =>
Failure(
new IllegalArgumentException(
s"Bad number of arguments: ${other.length}. Expected: 1"))
}
}
}
case class AcceptDLCOffer(offer: LnMessage[DLCOfferTLV]) case class AcceptDLCOffer(offer: LnMessage[DLCOfferTLV])
object AcceptDLCOffer extends ServerJsonModels { object AcceptDLCOffer extends ServerJsonModels {

View file

@ -627,6 +627,9 @@ sealed trait NumericEventDescriptorTLV extends EventDescriptorTLV {
case None => false case None => false
} }
} }
/** If the Descriptor contains negative values */
def isSigned: Boolean
} }
/** Describes a large range event using numerical decomposition */ /** Describes a large range event using numerical decomposition */
@ -705,7 +708,10 @@ case class SignedDigitDecompositionEventDescriptor(
numDigits: UInt16, numDigits: UInt16,
unit: NormalizedString, unit: NormalizedString,
precision: Int32) precision: Int32)
extends DigitDecompositionEventDescriptorV0TLV extends DigitDecompositionEventDescriptorV0TLV {
override val isSigned: Boolean = true
}
/** Represents a large range event that is unsigned */ /** Represents a large range event that is unsigned */
case class UnsignedDigitDecompositionEventDescriptor( case class UnsignedDigitDecompositionEventDescriptor(
@ -713,7 +719,9 @@ case class UnsignedDigitDecompositionEventDescriptor(
numDigits: UInt16, numDigits: UInt16,
unit: NormalizedString, unit: NormalizedString,
precision: Int32) precision: Int32)
extends DigitDecompositionEventDescriptorV0TLV extends DigitDecompositionEventDescriptorV0TLV {
override val isSigned: Boolean = false
}
object DigitDecompositionEventDescriptorV0TLV object DigitDecompositionEventDescriptorV0TLV
extends TLVFactory[DigitDecompositionEventDescriptorV0TLV] { extends TLVFactory[DigitDecompositionEventDescriptorV0TLV] {

View file

@ -226,6 +226,14 @@ the `-p 9999:9999` port mapping on the docker container to adjust for this.
- `passphrase` - The passphrase to encrypt the wallet with - `passphrase` - The passphrase to encrypt the wallet with
### DLC ### DLC
- `decodecontractinfo` `contractinfo` - Decodes a contract info into json
- `contractinfo` - Hex encoded contract info
- `decodeoffer` `offer` - Decodes an offer message into json
- `offer` - Hex encoded dlc offer message
- `decodeannouncement` `announcement` - Decodes an oracle announcement message into json
- `announcement` - Hex encoded oracle announcement message
- `decodeattestments` `attestments` - Decodes an oracle attestments message into json
- `attestments` - Hex encoded oracle attestments message
- `getdlchostaddress` - Returns the public listening address of the DLC Node - `getdlchostaddress` - Returns the public listening address of the DLC Node
- `createdlcoffer` `contractInfo` `collateral` `[feerate]` `locktime` `refundlocktime` - Creates a DLC offer that another party can accept - `createdlcoffer` `contractInfo` `collateral` `[feerate]` `locktime` `refundlocktime` - Creates a DLC offer that another party can accept
- `contractInfo` - Hex encoded contractInfo message - `contractInfo` - Hex encoded contractInfo message

View file

@ -37,7 +37,151 @@ Both parties must agree on all fields from the table below:
| refundlocktime | Locktime of the Refund Transaction | | refundlocktime | Locktime of the Refund Transaction |
| feerate | Fee rate in sats/vbyte | | feerate | Fee rate in sats/vbyte |
> Note: if you wish to set up your own oracle for testing, you can do so by checking out our [oracle rpc server](../oracle/oracle-server.md) or [Krystal Bull](https://github.com/benthecarman/krystal-bull) > Note: if you wish to set up your own oracle for testing, you can do so by checking out our [oracle rpc server](../oracle/oracle-server.md) or [Krystal Bull](https://github.com/bitcoin-s/krystal-bull)
### Decoding DLC Messages
With the cli tool you can decode dlc offers, contract infos, oracle announcements, and oracle attestations.
This can be helpful for when developing on top of bitcoin-s and not having the proper DLC tooling to understand the messages.
Examples:
Decoding Offer:
```bash
bitcoin-s-cli decodeoffer a71a006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000fdd82ee40000000000010f2cfda7101802035965730000000000010f2c024e6f0000000000000000fda712bcfdd824b8d89fe4d84bdf96299d2e3664acda7e14001297f341afcaca91d3909f12988cb308f309625d89c078bf2268b174faf1c082f1f10bef55834af50b91d9a241ffb2b8b005b07acf849ad2cec22107331dedbf5a607654fad4eafe39c278e27dde68fdd822540001ef8bd9f4445543c63923dde8c00f60d649c445a7772ac4693136b2fd234fcc2f60dfa880fdd80609000203596573024e6f20446f657320746865206d656d706f6f6c20636c656172206f6e20372f322f323102279cf17c387d7101b08d90b2aeef46231957d9d3558f5871e62781a68e7d75c9001600148dc81a3097bf28c6020eaf89cefae69c5f31aceb5d655d867008d23e00000000000090880001fda714fd01b3627231e09b7948c3019d02000000000102368af13baeef5a313d3ef12e895b232f1e76212cb76957d7e4b8a664915d15960100000000fdfffffffaec4ef43ae2365d0abc53336188053da4078a5b7489928602bcc8c9a4b75f0d0000000000fdffffff030f0b000000000000160014107eb2463ad25843ed01fd33656c83bdd11db3554a8701000000000022002002265c41f299b3c7f0dda5b9d7bc4135d25c2d8aed286837aa9e0954d70894d606961c000000000016001482a7ecd4624e6856d1b09c27e9f3a82323f49c2d0247304402200911162e99e23e4a26e0219a4bdaaf7a5f790be63a8376516640dcc0860ca4d602203a078371904842e2e6adfbebebcac138db23514b3396851569f5f2bf82c3197a012103cd2d0a4eace5993ebb0a75da85d9070627e64f1a5783cf5217e9bd82d20d1c470247304402200fa8515323410ca2b14b08bf22c618b6ced77b9289cb1dfa7ac10548e2e1b2e002206a407bcafdfb64009182fb5838db9671968abdd902e1194f6883cd0e5413ad36012103d3864eb755690e2b4ff139047419373e36a05630429da76fef5de25eeffb4ffc0000000000000002fffffffd006b000000160014d13be5f330b1ea9bbf61f68002b4465e02c341d9df27e1460161e002825311ec94d1e91b000000000000000a0000000061316580
{
"contractFlags": "0",
"chainHash": "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000",
"contractInfo": {
"totalCollateral": 69420,
"contractDescriptor": {
"outcomes": [
{
"outcome": "Yes",
"localPayout": 69420
},
{
"outcome": "No",
"localPayout": 0
}
]
},
"oracleInfo": {
"announcement": {
"announcementSignature": "d89fe4d84bdf96299d2e3664acda7e14001297f341afcaca91d3909f12988cb308f309625d89c078bf2268b174faf1c082f1f10bef55834af50b91d9a241ffb2",
"publicKey": "b8b005b07acf849ad2cec22107331dedbf5a607654fad4eafe39c278e27dde68",
"event": {
"nonces": [
"ef8bd9f4445543c63923dde8c00f60d649c445a7772ac4693136b2fd234fcc2f"
],
"maturity": "2021-07-03T00:00:00Z",
"descriptor": {
"outcomes": [
"Yes",
"No"
]
},
"eventId": "Does the mempool clear on 7/2/21"
}
}
}
},
"fundingPubKey": "02279cf17c387d7101b08d90b2aeef46231957d9d3558f5871e62781a68e7d75c9",
"payoutSPK": "1600148dc81a3097bf28c6020eaf89cefae69c5f31aceb",
"payoutSerialId": 6729888050161701888,
"offerCollateralSatoshis": 37000,
"fundingInputs": [
{
"inputSerialId": 7093787203812804608,
"prevTx": "02000000000102368af13baeef5a313d3ef12e895b232f1e76212cb76957d7e4b8a664915d15960100000000fdfffffffaec4ef43ae2365d0abc53336188053da4078a5b7489928602bcc8c9a4b75f0d0000000000fdffffff030f0b000000000000160014107eb2463ad25843ed01fd33656c83bdd11db3554a8701000000000022002002265c41f299b3c7f0dda5b9d7bc4135d25c2d8aed286837aa9e0954d70894d606961c000000000016001482a7ecd4624e6856d1b09c27e9f3a82323f49c2d0247304402200911162e99e23e4a26e0219a4bdaaf7a5f790be63a8376516640dcc0860ca4d602203a078371904842e2e6adfbebebcac138db23514b3396851569f5f2bf82c3197a012103cd2d0a4eace5993ebb0a75da85d9070627e64f1a5783cf5217e9bd82d20d1c470247304402200fa8515323410ca2b14b08bf22c618b6ced77b9289cb1dfa7ac10548e2e1b2e002206a407bcafdfb64009182fb5838db9671968abdd902e1194f6883cd0e5413ad36012103d3864eb755690e2b4ff139047419373e36a05630429da76fef5de25eeffb4ffc00000000",
"prevTxVout": 2,
"sequence": 4294967293,
"maxWitnessLen": 107,
"redeemScript": null
}
],
"changeSPK": "160014d13be5f330b1ea9bbf61f68002b4465e02c341d9",
"changeSerialId": 1.6080068685336797E19,
"fundOutputSerialId": 9.390869355804355E18,
"feeRatePerVb": 10,
"cetLocktime": 0,
"refundLocktime": 1630627200
}
```
Decoding Contract Info:
```bash
bitcoin-s-cli decodecontractinfo fdd82ee40000000000010f2cfda7101802035965730000000000010f2c024e6f0000000000000000fda712bcfdd824b8d89fe4d84bdf96299d2e3664acda7e14001297f341afcaca91d3909f12988cb308f309625d89c078bf2268b174faf1c082f1f10bef55834af50b91d9a241ffb2b8b005b07acf849ad2cec22107331dedbf5a607654fad4eafe39c278e27dde68fdd822540001ef8bd9f4445543c63923dde8c00f60d649c445a7772ac4693136b2fd234fcc2f60dfa880fdd80609000203596573024e6f20446f657320746865206d656d706f6f6c20636c656172206f6e20372f322f3231
{
"totalCollateral": 69420,
"contractDescriptor": {
"outcomes": [
{
"outcome": "Yes",
"localPayout": 69420
},
{
"outcome": "No",
"localPayout": 0
}
]
},
"oracleInfo": {
"announcement": {
"announcementSignature": "d89fe4d84bdf96299d2e3664acda7e14001297f341afcaca91d3909f12988cb308f309625d89c078bf2268b174faf1c082f1f10bef55834af50b91d9a241ffb2",
"publicKey": "b8b005b07acf849ad2cec22107331dedbf5a607654fad4eafe39c278e27dde68",
"event": {
"nonces": [
"ef8bd9f4445543c63923dde8c00f60d649c445a7772ac4693136b2fd234fcc2f"
],
"maturity": "2021-07-03T00:00:00Z",
"descriptor": {
"outcomes": [
"Yes",
"No"
]
},
"eventId": "Does the mempool clear on 7/2/21"
}
}
}
```
Decoding Oracle Announcement:
```bash
bitcoin-s-cli decodeannouncement fdd824b1585e18c3bc1d922854329fdb5a402713b161b7baebb6b6f3249936ef79c0a6671027afd7a1126d79e589811042eb4885c0cb7c150b4c4863a1e35a2ac432f7c81d5dcdba2e64cb116cc0c375a0856298f0058b778f46bfe625ac6576204889e4fdd8224d0001424c11a44c2e522f90bbe4abab6ec1bc8ab44c9b29316ce6e1d0d7d08385a474603c2e80fdd80609000203594553024e4f194254432d5553442d4f5645522d35304b2d434f494e42415345
{
"announcementSignature": "585e18c3bc1d922854329fdb5a402713b161b7baebb6b6f3249936ef79c0a6671027afd7a1126d79e589811042eb4885c0cb7c150b4c4863a1e35a2ac432f7c8",
"publicKey": "1d5dcdba2e64cb116cc0c375a0856298f0058b778f46bfe625ac6576204889e4",
"event": {
"nonces": [
"424c11a44c2e522f90bbe4abab6ec1bc8ab44c9b29316ce6e1d0d7d08385a474"
],
"maturity": "2021-03-01T00:00:00Z",
"descriptor": {
"outcomes": [
"YES",
"NO"
]
},
"eventId": "BTC-USD-OVER-50K-COINBASE"
}
}
```
Decoding Oracle Attestations
```bash
bitcoin-s-cli decodeattestments fdd8687f194254432d5553442d4f5645522d35304b2d434f494e424153451d5dcdba2e64cb116cc0c375a0856298f0058b778f46bfe625ac6576204889e40001424c11a44c2e522f90bbe4abab6ec1bc8ab44c9b29316ce6e1d0d7d08385a474de6b75f1da183a2a4f9ad144b48bf1026cee9687221df58f04128db79ca17e2a024e4f
{
"eventId": "BTC-USD-OVER-50K-COINBASE",
"signatures": [
"424c11a44c2e522f90bbe4abab6ec1bc8ab44c9b29316ce6e1d0d7d08385a474de6b75f1da183a2a4f9ad144b48bf1026cee9687221df58f04128db79ca17e2a"
],
"values": [
"NO"
]
}
```
## Step 3: Set up The DLC ## Step 3: Set up The DLC
@ -175,4 +319,3 @@ If the `refundlocktime` for the DLC has been reached, you can get the fully-sign
```bashrc ```bashrc
./app/cli/target/universal/stage/bitcoin-s-cli executedlcrefund [contractId] ./app/cli/target/universal/stage/bitcoin-s-cli executedlcrefund [contractId]
``` ```