mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-23 14:50:42 +01:00
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:
parent
40f89af597
commit
515e85b1c5
8 changed files with 602 additions and 9 deletions
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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) =>
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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] {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue