2021 11 30 issue 3847 (#3862)

* Start pulling over accept serializers from dlc message spec PR

* Get sign tlv json serialization working

* Implement decodeaccept,decodesign in CoreRoutes

* Move messages to DLCTestUtil, add documentation
This commit is contained in:
Chris Stewart 2021-12-01 13:30:18 -06:00 committed by GitHub
parent 169222a306
commit d393848cc2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 664 additions and 11 deletions

View File

@ -0,0 +1,60 @@
package org.bitcoins.commons.json
import org.bitcoins.commons.serializers.Picklers
import org.bitcoins.core.protocol.tlv.DLCAcceptTLV
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
class DLCAcceptJsonSerializerTest extends BitcoinSUnitTest {
behavior of "DLCAcceptJsonSerializer"
private val testString: String =
s"""
|{
| "temporaryContractId": "bdc5286cd4b56c2b2525bc9e0e3dda1d6a2a130cc7fd8ca8a38d401ef9a5d3e7",
| "acceptCollateral": 100000000,
| "fundingPubkey": "0208dfffdda2a61c78c906f5d76afdb0b8fe0555e6a3644c41f53b511427f80f0a",
| "payoutSpk": "001463c84b34fcf37ee58566aa6daf0747f74e509970",
| "payoutSerialId": "11859227771650291066",
| "fundingInputs": [
| {
| "inputSerialId": "6134040349072004330",
| "prevTx": "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03510101ffffffff0200f2052a01000000160014c87d38bcd3a468680e7c0abeeb7821d6302df9120000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
| "prevTxVout": 0,
| "sequence": 4294967295,
| "maxWitnessLen": 107,
| "redeemScript": ""
| }
| ],
| "changeSpk": "001481467abc1d30f5139fe8ff8c1ca7f7cb3f5bc031",
| "changeSerialId": "13987689245506757418",
| "cetAdaptorSignatures": {
| "ecdsaAdaptorSignatures": [
| {
| "signature": "02b7dda1b4030e0f85a98eb15a6806f1ffb72b578a508f671f4e6bbd954aa2d5b9022a01536f70340da9ba2b0e034deec1a0b658cbec2432f2fa2a96de09000eafaafb586eec1375c85737bb7e9fde1cb7fcf3a8a970e698d0e5da55297ea64a45b8ffb2b705974b91e8e3d9b0e46573c648122fda1ef941980ba845ab09d1e7a43949add788ed79e4a23b832b1418c3b54251b0d3c83e791ce24c2e30544456d3b8"
| },
| {
| "signature": "02d510a07687553f7dd26ff6124cdccbf73ccd8661ae9bd6a59a25cedac04727fd03fb8955cb520fde3dc30fbf095054a87f8c3e87f027238e3bc435b0dc6df2532af475c531e234ee045d6119d989a62d8b29bdfdbdcdf8d6761b0cb5fbd1eb6ef71fa04c7a993801e2d02d5659c08f629f7d5ef02173ce5b1fcec361d99b9aee79549a085c14bb3f7df507e891a35089b3d9886e7c81b7367cac8c75bbb9432861"
| },
| {
| "signature": "0328ca81f2f281c39eda04fb69456f6b104f09d920d57def9e396aa60de6c0b38c03e73ec3891966e5d339ea154bf17e265f80cb75bfc922b83d79102f44479a69e281a485c8e99abc382aad656983f8f92a5836437156c783e261bfe0bf7eec9e3bb83ff8b4d948458b3d8fdfbc74b23cd02a72b06b84aa156a7bf6a578757c205257aa86a728b6b8022c303c9b01164f0332d6bc5aaa17014c7bb87d598b83af5d"
| },
| {
| "signature": "0337e4ce2c5b3fb9d70663bac8797f8e7a1493af23f74ac9ace7449a35d59c757a02676f9cc3adf9d3d03ef5a4daf704c5d178dae12364f0297ab09982d5da08145010fc26c71166e6a2e651e4c351916f2e7d538c14adeccbd2f21c18f1d4ddea619120af9bcef67dcda0ed5b8d5c4dddee167107263becf05c91e0a13c2e653cd9722674531031610ef6b8cd80b205fc78834db55cf08e45d37616a6e218b7e09c"
| }
| ]
| },
| "refundSignature": "304402202c9b25719f0a22d7372c54c36f916e44f445135809433585636417fe18b9316c0220125584711c7238d0adf4a494e30a166fef35edf3d0d1718955abd73b76a26e9d",
| "negotiationFields": null
| }
|""".stripMargin
it must "have serialization symmetry for a accept json message" in {
val accept = upickle.default.read[DLCAcceptTLV](testString)(
Picklers.dlcAcceptTLVPickler)
val json: String =
upickle.default.write(accept)(Picklers.dlcAcceptTLVPickler)
assert(json == testString.replaceAll("\\s", ""))
}
}

View File

@ -0,0 +1,57 @@
package org.bitcoins.commons.json
import org.bitcoins.commons.serializers.Picklers
import org.bitcoins.core.protocol.tlv.{DLCSignTLV, LnMessage}
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
class DLCSignJsonSerializerTest extends BitcoinSUnitTest {
behavior of "DLCAcceptJsonSerializer"
private val testString: String = {
s"""
|{
| "contractId": "afcd7e786c0b9085784a6d063c7f4e7291784f9be3ba23cd8734559040fd2202",
| "cetAdaptorSignatures": {
| "ecdsaAdaptorSignatures": [
| {
| "signature": "02feee4c75948830549fd19efd93cd4e00d4f538041ecf2f2cb6820c78c0a57fc9034d9ce5849e0abe235b8c3137506745508aa17c7a3064ec4172e76c88e66ca27257c5ec09f99201c9bc95b8ab345aff9b32b89c328d21a6ca5553f287a650320162381ef95042d9ac57f66f39dc6904f17a472671d3828f75ef29fbddcd31119b69adb216dac9b5960b91b3e3d83b9310484ef667d19f4bed01964ff063a2acbd"
| },
| {
| "signature": "025e840169038f0d046cc5005d9d622fd19ec2dd84d15a022f3eff4a781c6f404602cf8ec975191b73ecc3944786f5cca2a01ba3e71bbde9ea6932083c23591dfb6e3e06489cde6415c39cc9e187283f7a9b76227b157572dc62f1a5b130b4fc6b68c14ea07deddb2865d634a244839a87f158145c59d9aecb51b082c3a2ef0cb0201bc65252a3876f7253855ce4d3e72cd650bc275dd878f920162af74865b0346b"
| },
| {
| "signature": "03ea132cd20738f1a6640f98dc5664d12952e000feb69bbb3c4929c9054b3ca965030b788e83098f906cd3b088ad297e8149d08eb02442f42bd2b9e4455d5a19a96fd329625e1f1d0db9b9f625a6242f1fc53f56ec8090575c704e364518bdf7d690386d0f4a7bfea6c263c5fbc91631b0f0323a92c51f43f3228c9649c5441b338f2e37cbad0d10ce0d80429e2a6a528e4a0e9700188b86c267246b5049ce7da031"
| },
| {
| "signature": "02c2ec44af74653bdad7db89a38ab61d3dabf0646acdfab1bd5906d4a75f8d7a0c03051281626d4c8c84a5b7d09717c69f1c5340300be5ef6d94a4efe4d1baebbf820f8891a3b86d0f4a9a42d3fa0a44f7d711a5e65cc887b0424530e9b7e49f8d3a21d89762d535c7f121bc057b01b67372ea19762fad714580767ea6a091771808c3806b273382e0792573f624a90e15004a3b852e627f422c07209fd876c38169"
| }
| ]
| },
| "refundSignature": "3044022005d4dcc449db1a5997b82f2aabaffb3414ad1e10c159fc3fcbd6dab121a3cb98022037583d39f66aa347a8170dba12cf88102997f460bb27d70365f7f85cd4ce5ee2",
| "fundingSignatures": {
| "fundingSignatures": [
| {
| "witnessElements": [
| {
| "witness": "304402204bbe45fd65402ad89784062c2af0f0c88a55f1de4509e4491f7933ada30fbab102207144fe9b8d621d2bef5c226d6f2f8ff43548f4c0bb9ea221a0c2a034a069c74f01"
| },
| {
| "witness": "03bdb496d31a30d8e87ab7767fd26bfdfa7f6d06a0cfaea082a91af1ae1814f251"
| }
| ]
| }
| ]
| }
| }
|""".stripMargin
}
it must "have serialization symmetry for dlc sign messages" in {
val sign =
upickle.default.read[DLCSignTLV](testString)(Picklers.dlcSignTLVPickler)
println(s"accept=${LnMessage(sign).hex}")
val json: String =
upickle.default.write(sign)(Picklers.dlcSignTLVPickler)
assert(json == testString.replaceAll("\\s", ""))
}
}

View File

@ -7,9 +7,15 @@ import org.bitcoins.core.crypto._
import org.bitcoins.core.currency.{Bitcoins, Satoshis}
import org.bitcoins.core.dlc.accounting.DLCWalletAccounting
import org.bitcoins.core.hd.AddressType
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.number.{UInt16, UInt32, UInt64}
import org.bitcoins.core.protocol.dlc.models.DLCStatus._
import org.bitcoins.core.protocol.dlc.models._
import org.bitcoins.core.protocol.script.{
ScriptPubKey,
ScriptWitness,
ScriptWitnessV0,
WitnessScriptPubKey
}
import org.bitcoins.core.protocol.tlv._
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionOutPoint}
import org.bitcoins.core.protocol.{BitcoinAddress, BlockStamp}
@ -123,16 +129,234 @@ object Picklers {
implicit val lnMessageDLCOfferTLVPickler: ReadWriter[LnMessage[DLCOfferTLV]] =
readwriter[String].bimap(_.hex, LnMessageFactory(DLCOfferTLV).fromHex)
implicit val dlcAcceptTLVPickler: ReadWriter[DLCAcceptTLV] =
readwriter[String].bimap(_.hex, DLCAcceptTLV.fromHex)
private def parseU64(str: ujson.Str): UInt64 = {
UInt64(BigInt(str.str))
}
private def parseFundingInput(obj: ujson.Obj): FundingInputTLV = {
val inputSerialId = parseU64(obj(PicklerKeys.inputSerialIdKey).str)
val prevTx = Transaction.fromHex(obj(PicklerKeys.prevTxKey).str)
val prevTxVout = obj(PicklerKeys.prevTxVoutKey).num.toLong
val sequence = UInt32(obj(PicklerKeys.sequenceKey).num.toLong)
val maxWitnessLen = UInt16(obj(PicklerKeys.maxWitnessLenKey).num.toLong)
val redeemScriptStr = obj(PicklerKeys.redeemScriptKey).str
val redeemScriptOpt = if (redeemScriptStr.nonEmpty) {
val spk =
WitnessScriptPubKey.fromAsmHex(obj(PicklerKeys.redeemScriptKey).str)
Some(spk)
} else {
None
}
FundingInputV0TLV(
inputSerialId,
prevTx,
UInt32(prevTxVout),
sequence,
maxWitnessLen,
redeemScriptOpt
)
}
private def parseFundingInputs(arr: ujson.Arr): Vector[FundingInputTLV] = {
arr.value.toVector.map {
case inputObj: ujson.Obj =>
parseFundingInput(inputObj)
case x: ujson.Value =>
sys.error(s"Expected obj, got=$x")
}
}
private def parseCetAdaptorSignatures(obj: ujson.Obj): CETSignaturesTLV = {
val ecAdaptorSignaturesArr = obj(PicklerKeys.ecdsaAdaptorSignaturesKey).arr
val adaptorSigs = parseAdaptorSignatures(ecAdaptorSignaturesArr)
CETSignaturesV0TLV(adaptorSigs)
}
private def parseAdaptorSignatures(
arr: ujson.Arr): Vector[ECAdaptorSignature] = {
arr.value.toVector.map {
case obj: ujson.Obj =>
ECAdaptorSignature.fromHex(obj(PicklerKeys.signatureKey).str)
case x: ujson.Value =>
sys.error(s"Excpected string for ecdsa adaptor siganture, got obj=$x")
}
}
private def writeAdaptorSignatures(
sigs: Vector[ECAdaptorSignature]): Vector[ujson.Obj] = {
sigs.map { sig =>
ujson.Obj(PicklerKeys.signatureKey -> Str(sig.hex))
}
}
private def writeCetAdaptorSigs(
cetSignaturesTLV: CETSignaturesTLV): ujson.Obj = {
cetSignaturesTLV match {
case v0: CETSignaturesV0TLV =>
val sigsVec = writeAdaptorSignatures(v0.sigs)
ujson.Obj(
PicklerKeys.ecdsaAdaptorSignaturesKey -> ujson.Arr.from(sigsVec))
}
}
private def readAcceptTLV(obj: ujson.Obj): DLCAcceptTLV = {
val tempContractId =
Sha256Digest.fromHex(obj(PicklerKeys.temporaryContractIdKey).str)
val acceptCollateral = Satoshis(
obj(PicklerKeys.acceptCollateralKey).num.toLong)
val fundingPubKey =
ECPublicKey.fromHex(obj(PicklerKeys.fundingPubKeyKey).str)
val payoutSpk = ScriptPubKey.fromAsmHex(obj(PicklerKeys.payoutSpkKey).str)
val payoutSerialId = parseU64(obj(PicklerKeys.payoutSerialIdKey).str)
val fundingInputs = parseFundingInputs(
obj(PicklerKeys.fundingInputsKey).arr)
val changeSpk = ScriptPubKey.fromAsmHex(obj(PicklerKeys.changeSpkKey).str)
val changeSerialId = parseU64(obj(PicklerKeys.changeSerialIdKey).str)
val cetAdaptorSigs = parseCetAdaptorSignatures(
obj(PicklerKeys.cetAdaptorSignaturesKey).obj)
val refundSignature =
ECDigitalSignature.fromHex(obj(PicklerKeys.refundSignatureKey).str)
val negotiationFields = {
obj(PicklerKeys.negotiationFieldsKey).strOpt match {
case Some(str) =>
sys.error(s"Don't know how to parse negotiation fields, got=$str")
case None => NegotiationFieldsTLV.empty
}
}
val acceptTLV = DLCAcceptTLV(
tempContractId = tempContractId,
totalCollateralSatoshis = acceptCollateral,
fundingPubKey = fundingPubKey,
payoutSPK = payoutSpk,
payoutSerialId = payoutSerialId,
fundingInputs = fundingInputs,
changeSPK = changeSpk,
changeSerialId = changeSerialId,
cetSignatures = cetAdaptorSigs,
refundSignature = refundSignature,
negotiationFields = negotiationFields
)
acceptTLV
}
private def writeAcceptTLV(accept: DLCAcceptTLV): ujson.Obj = {
Obj(
PicklerKeys.tempContractIdKey -> Str(accept.tempContractId.hex),
PicklerKeys.acceptCollateralKey -> Num(
accept.totalCollateralSatoshis.toLong.toDouble),
PicklerKeys.fundingPubKeyKey -> Str(accept.fundingPubKey.hex),
PicklerKeys.payoutSpkKey -> Str(accept.payoutSPK.asmHex),
PicklerKeys.payoutSerialIdKey -> Str(
accept.payoutSerialId.toBigInt.toString()),
PicklerKeys.fundingInputsKey -> writeJs(accept.fundingInputs),
PicklerKeys.changeSpkKey -> Str(accept.changeSPK.asmHex),
PicklerKeys.changeSerialIdKey -> Str(
accept.changeSerialId.toBigInt.toString()),
PicklerKeys.cetAdaptorSignaturesKey -> writeCetAdaptorSigs(
accept.cetSignatures),
PicklerKeys.refundSignatureKey -> Str(accept.refundSignature.hex),
PicklerKeys.negotiationFieldsKey -> ujson.Null
)
}
private def parseFundingSignatures(obj: ujson.Obj): FundingSignaturesTLV = {
val fundingSignatures: Vector[ujson.Value] = obj(
PicklerKeys.fundingSignaturesKey).arr.toVector
val witV0 = paresFundingSignaturesArr(fundingSignatures)
FundingSignaturesV0TLV(witV0)
}
private def paresFundingSignaturesArr(
arr: Vector[ujson.Value]): Vector[ScriptWitnessV0] = {
arr.map {
case obj: ujson.Obj =>
val witnessElementsArr = obj(PicklerKeys.witnessElementsKey).arr
val witnesses: Vector[ByteVector] = {
parseWitnessElements(witnessElementsArr)
}
val scriptWitnessV0 = ScriptWitness
.apply(witnesses.reverse)
.asInstanceOf[ScriptWitnessV0]
scriptWitnessV0
case x =>
sys.error(s"Expected array of objects for funding signatures, got=$x")
}
}
private def parseWitnessElements(arr: ujson.Arr): Vector[ByteVector] = {
arr.value.toVector.map {
case obj: ujson.Obj =>
val witnessStr = obj(PicklerKeys.witnessKey).str
ByteVector.fromValidHex(witnessStr)
case x: ujson.Value =>
sys.error(s"Expected witness json object, got=$x")
}
}
private def writeWitnessElements(witness: ScriptWitness): ujson.Obj = {
val vec: Vector[ujson.Obj] = witness.stack.reverse.map { w =>
ujson.Obj(PicklerKeys.witnessKey -> Str(w.toHex))
}.toVector
ujson.Obj(PicklerKeys.witnessElementsKey -> ujson.Arr.from(vec))
}
private def writeFundingSignatures(
fundingSigs: FundingSignaturesTLV): ujson.Obj = {
val sigs: Vector[ujson.Obj] = fundingSigs match {
case v0: FundingSignaturesV0TLV =>
val witnessJson: Vector[ujson.Obj] =
v0.witnesses.map(writeWitnessElements)
witnessJson
}
ujson.Obj(
PicklerKeys.fundingSignaturesKey -> ujson.Arr.from(sigs)
)
}
private def readSignTLV(obj: ujson.Obj): DLCSignTLV = {
val contractId = ByteVector.fromValidHex(obj(PicklerKeys.contractIdKey).str)
val adaptorSigs = parseCetAdaptorSignatures(
obj(PicklerKeys.cetAdaptorSignaturesKey).obj)
val refundSignature =
ECDigitalSignature.fromHex(obj(PicklerKeys.refundSignatureKey).str)
val fundingSignatures = parseFundingSignatures(
obj(PicklerKeys.fundingSignaturesKey).obj)
val signTLV =
DLCSignTLV(contractId, adaptorSigs, refundSignature, fundingSignatures)
signTLV
}
private def writeSignTLV(sign: DLCSignTLV): ujson.Obj = {
ujson.Obj(
PicklerKeys.contractIdKey -> sign.contractId.toHex,
PicklerKeys.cetAdaptorSignaturesKey -> writeCetAdaptorSigs(
sign.cetSignatures),
PicklerKeys.refundSignatureKey -> ujson.Str(sign.refundSignature.hex),
PicklerKeys.fundingSignaturesKey ->
writeFundingSignatures(sign.fundingSignatures)
)
}
implicit val dlcAcceptTLVPickler: ReadWriter[DLCAcceptTLV] = {
readwriter[ujson.Obj].bimap(writeAcceptTLV, readAcceptTLV)
}
implicit val dlcSignTLVPickler: ReadWriter[DLCSignTLV] = {
readwriter[ujson.Obj].bimap(writeSignTLV, readSignTLV)
}
implicit val lnMessageDLCAcceptTLVPickler: ReadWriter[
LnMessage[DLCAcceptTLV]] =
readwriter[String].bimap(_.hex, LnMessageFactory(DLCAcceptTLV).fromHex)
implicit val dlcSignTLVPickler: ReadWriter[DLCSignTLV] =
readwriter[String].bimap(_.hex, DLCSignTLV.fromHex)
implicit val lnMessageDLCSignTLVPickler: ReadWriter[LnMessage[DLCSignTLV]] =
readwriter[String].bimap(_.hex, LnMessageFactory(DLCSignTLV).fromHex)
@ -227,11 +451,11 @@ object Picklers {
val redeemScriptJson = redeemScriptOpt match {
case Some(rs) => Str(rs.hex)
case None => ujson.Null
case None => Str("")
}
Obj(
"inputSerialId" -> Num(inputSerialId.toBigInt.toDouble),
"inputSerialId" -> Str(inputSerialId.toBigInt.toString()),
"prevTx" -> Str(prevTx.hex),
"prevTxVout" -> Num(prevTxVout.toLong.toDouble),
"sequence" -> Num(sequence.toLong.toDouble),
@ -433,7 +657,7 @@ object Picklers {
totalCollateralSatoshis.toLong.toDouble),
"fundingInputs" -> fundingInputs.map(i => writeJs(i)),
"changeSPK" -> Str(changeSPK.hex),
"changeSerialId" -> Num(changeSerialId.toBigInt.toDouble),
"changeSerialId" -> Str(changeSerialId.toBigInt.toString()),
"fundOutputSerialId" -> Num(fundOutputSerialId.toBigInt.toDouble),
"feeRatePerVb" -> Num(feeRate.toLong.toDouble),
"cetLocktime" -> Num(contractMaturityBound.toUInt32.toLong.toDouble),

View File

@ -0,0 +1,46 @@
package org.bitcoins.server
import akka.http.scaladsl.model.ContentTypes
import akka.http.scaladsl.testkit.ScalatestRouteTest
import org.bitcoins.server.routes.ServerCommand
import org.bitcoins.testkit.BitcoinSTestAppConfig
import org.bitcoins.testkitcore.dlc.DLCTestUtil
import org.scalamock.scalatest.MockFactory
import org.scalatest.wordspec.AnyWordSpec
class CoreRoutesSpec
extends AnyWordSpec
with ScalatestRouteTest
with MockFactory {
implicit val conf: BitcoinSAppConfig =
BitcoinSTestAppConfig.getSpvTestConfig()
val coreRoutes = CoreRoutes()
"Core routes" should {
"decode an accept message" in {
val args = ujson.Arr(DLCTestUtil.acceptHex)
val route =
coreRoutes.handleCommand(ServerCommand("decodeaccept", args))
Post() ~> route ~> check {
assert(contentType == ContentTypes.`application/json`)
val actualJson = ujson.read(responseAs[String])
assert(actualJson == DLCTestUtil.expectedAccept)
}
}
"decode a sign message" in {
val args = ujson.Arr(DLCTestUtil.signHex)
val route =
coreRoutes.handleCommand(ServerCommand("decodesign", args))
Post() ~> route ~> check {
assert(contentType == ContentTypes.`application/json`)
val actualJson = ujson.read(responseAs[String])
assert(actualJson == DLCTestUtil.expectedSign)
}
}
}
}

View File

@ -24,7 +24,7 @@ case class CoreRoutes()(implicit system: ActorSystem, config: BitcoinSAppConfig)
extends ServerRoute {
import system.dispatcher
def handleCommand: PartialFunction[ServerCommand, Route] = {
override def handleCommand: PartialFunction[ServerCommand, Route] = {
case ServerCommand("finalizepsbt", arr) =>
withValidServerCommand(FinalizePSBT.fromJsArr(arr)) {
case FinalizePSBT(psbt) =>
@ -153,7 +153,20 @@ case class CoreRoutes()(implicit system: ActorSystem, config: BitcoinSAppConfig)
Server.httpSuccess(writeJs(offerTLV))
}
}
case ServerCommand("decodeaccept", arr) =>
withValidServerCommand(DecodeAccept.fromJsArr(arr)) {
case DecodeAccept(accept) =>
complete {
Server.httpSuccess(writeJs(accept))
}
}
case ServerCommand("decodesign", arr) =>
withValidServerCommand(DecodeSign.fromJsArr(arr)) {
case DecodeSign(accept) =>
complete {
Server.httpSuccess(writeJs(accept))
}
}
case ServerCommand("decodecontractinfo", arr) =>
withValidServerCommand(DecodeContractInfo.fromJsArr(arr)) {
case DecodeContractInfo(contractInfo) =>

View File

@ -735,6 +735,66 @@ object DecodeOffer extends ServerJsonModels {
}
}
case class DecodeAccept(accept: DLCAcceptTLV)
object DecodeAccept extends ServerJsonModels {
def fromJsArr(jsArr: ujson.Arr): Try[DecodeAccept] = {
jsArr.arr.toList match {
case acceptJs :: Nil =>
Try {
val accept: LnMessage[DLCAcceptTLV] =
LnMessageFactory(DLCAcceptTLV).fromHex(acceptJs.str)
DecodeAccept(accept.tlv)
} match {
case Success(value) =>
Success(value)
case Failure(_) =>
Try {
val accept = DLCAcceptTLV.fromHex(acceptJs.str)
DecodeAccept(accept)
}
}
case Nil =>
Failure(new IllegalArgumentException(s"Missing accept announcement"))
case other =>
Failure(
new IllegalArgumentException(
s"Bad number of arguments: ${other.length} Expected: 1"))
}
}
}
case class DecodeSign(sign: DLCSignTLV)
object DecodeSign extends ServerJsonModels {
def fromJsArr(jsArr: ujson.Arr): Try[DecodeSign] = {
jsArr.arr.toList match {
case signJs :: Nil =>
Try {
val accept: LnMessage[DLCSignTLV] =
LnMessageFactory(DLCSignTLV).fromHex(signJs.str)
DecodeSign(accept.tlv)
} match {
case Success(value) =>
Success(value)
case Failure(_) =>
Try {
val sign = DLCSignTLV.fromHex(signJs.str)
DecodeSign(sign)
}
}
case Nil =>
Failure(new IllegalArgumentException(s"Missing accept announcement"))
case other =>
Failure(
new IllegalArgumentException(
s"Bad number of arguments: ${other.length} Expected: 1"))
}
}
}
case class DecodeAnnouncement(announcement: OracleAnnouncementTLV)
object DecodeAnnouncement extends ServerJsonModels {

View File

@ -32,4 +32,5 @@ abstract class Script extends NetworkElement {
/** The full byte serialization for a script on the network */
override val bytes: ByteVector = compactSizeUInt.bytes ++ asmBytes
lazy val asmHex: String = asmBytes.toHex
}

View File

@ -1535,6 +1535,8 @@ sealed trait NegotiationFieldsTLV extends DLCSetupPieceTLV
object NegotiationFieldsTLV extends TLVParentFactory[NegotiationFieldsTLV] {
final val empty: NoNegotiationFieldsTLV.type = NoNegotiationFieldsTLV
override val allFactories: Vector[TLVFactory[NegotiationFieldsTLV]] =
Vector(NoNegotiationFieldsTLVFactory, NegotiationFieldsV1TLV)

View File

@ -21,9 +21,96 @@ object PicklerKeys {
//offers
final val protocolVersionKey: String = "protocolVersion"
//accepts
final val tempContractIdKey: String = "temporaryContractId"
final val fundingPubKeyKey: String = "fundingPubkey"
final val acceptCollateralKey: String = "acceptCollateral"
final val payoutSpkKey: String = "payoutSpk"
final val payoutSerialIdKey: String = "payoutSerialId"
final val fundingInputsKey: String = "fundingInputs"
final val changeSpkKey = "changeSpk"
final val changeSerialIdKey: String = "changeSerialId"
final val negotiationFieldsKey: String = "negotiationFields"
//contract info
final val totalCollateralKey = "totalCollateral"
final val contractDescriptorKey = "contractDescriptor"
final val oracleInfoKey = "oracleInfo"
final val pairsKey = "pairs"
val contractFlagsKey = "contractFlags"
val chainHashKey = "chainHash"
val contractInfoKey = "contractInfo"
val singleContractInfoKey = "singleContractInfo"
val enumeratedContractDescriptorKey = "enumeratedContractDescriptor"
val numericOutcomeContractDescriptorKey = "numericOutcomeContractDescriptor"
val payoutsKey = "payouts"
//numeric contract descriptor
val numDigitsKey = "numDigits"
val payFunctionKey = "payoutFunction"
val payoutFunctionPiecesKey = "payoutFunctionPieces"
val leftEndPointKey = "leftEndPoint"
val eventOutcomeKey = "eventOutcome"
val outcomePayoutKey = "outcomePayout"
val payoutCurvePieceKey = "payoutCurvePiece"
val polynomialPayoutCurvePieceKey = "polynomialPayoutCurvePiece"
val payoutPointsKey = "payoutPoints"
val lastEndpointKey = "lastEndpoint"
val roundingIntervalsKey = "roundingIntervals"
val intervalsKey = "intervals"
val beginIntervalKey = "beginInterval"
val roundingModKey = "roundingMod"
val singleKey = "single"
val oracleAnnouncementKey = "oracleAnnouncement"
val announcementSignatureKey = "announcementSignature"
val oraclePublicKeyKey = "oraclePublicKey"
val oracleEventKey = "oracleEvent"
val oracleNoncesKey = "oracleNonces"
val eventMaturityEpochKey = "eventMaturityEpoch"
val eventDescriptorKey = "eventDescriptor"
val enumEventKey = "enumEvent"
val digitDecompositionEventKey = "digitDecompositionEvent"
val baseKey = "base"
val isSignedKey = "isSigned"
val unitKey = "unit"
val precisionKey = "precision"
val nbDigitsKey = "nbDigits"
val eventIdKey = "eventId"
val offerCollateralKey = "offerCollateral"
val fundOutputSerialIdKey = "fundOutputSerialId"
val feeRatePerKbKey = "feeRatePerVb"
val contractMaturityBoundKey = "contractMaturityBound"
val contractTimeoutKey = "contractTimeout"
val acceptMessageKey = "accept_message"
val temporaryContractIdKey = "temporaryContractId"
val inputSerialIdKey = "inputSerialId"
val prevTxKey = "prevTx"
val prevTxVoutKey = "prevTxVout"
val sequenceKey = "sequence"
val maxWitnessLenKey = "maxWitnessLen"
val redeemScriptKey = "redeemScript"
val cetAdaptorSignaturesKey = "cetAdaptorSignatures"
val ecdsaAdaptorSignaturesKey = "ecdsaAdaptorSignatures"
val signatureKey = "signature"
val refundSignatureKey = "refundSignature"
val signMessageKey = "sign_message"
val contractIdKey = "contractId"
val fundingSignaturesKey = "fundingSignatures"
val witnessElementsKey = "witnessElements"
val witnessKey = "witness"
val serializedKey = "serialized"
}

View File

@ -241,6 +241,10 @@ the `-p 9999:9999` port mapping on the docker container to adjust for this.
- `contractinfo` - Hex encoded contract info
- `decodeoffer` `offer` - Decodes an offer message into json
- `offer` - Hex encoded dlc offer message
- `decodeaccept` `accept` - Decodes an accept message into json
- `accept` - Hex encoded dlc accept message
- `decodesign` `sign` - Decodes a sign message into json
- `sign` - Hex encoded dlc sign 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

View File

@ -99,4 +99,103 @@ object DLCTestUtil {
val remoteInfo = info.flip(totalCollateral.satoshis)
(info, remoteInfo)
}
val acceptHex: String =
"a71cd1dc7596d2a2f14a2542f2b7e563c149c910ba2238b59532b9fc1ebf6f8465f1000000000000ea60037dbfd3eb9296d1bd537c4008794989893821d48a163b0f3b7b01433599e8121100160014cb62b501146622502837f5cd898a657feba867eb9701e48dde0387770001fda714f4e2f7667f1f20d72700de02000000000101433df2df0297e1de67283fd8d369a16be5179f3a696fe36bf601337426408af50100000000000000000280f0fa0200000000160014d431e458cc852e7f2f21a6055751b5d2f30b7bcac190f30200000000160014aba424ac1cdf6c4ff53dcc76c1458c2e8bb7c71702473044022049911e914325d2c21f1f7a52d0bef9f50362cf130a87d73a30e61863fa72582502205338e9fa57b6172c821cb9e2530e7b0bf27812e2110ffa7adf277da1be31872d012103cf091ac1820aabeb4399d63bacb5b1c58310166a8b9466bc5efb4dbb678aba1e0000000000000000ffffffff006b0000001600145097a3a31e88036a7396728b6e3ea47c70780f8db68e98eec3ad4f26fda716fd01e70302834a87e2019a7c431db58235abd00ce62ca5a36e8b5e60bc6dd895045e9d936302e00d2ff673cf117f8bc3e6cbbbdc9efeb6b641d55c32c58ca46422c7279db2a3d07fa999360ba675ad6d342d21997e8644f60509a106df667f85f948f1d73ca5de082db23a50f07d9180e253bd8879567d1e4110f07de6f57ef75cf32206edc4d0656f6dcd738f0301d4baf73182aac102a1917b9688dcb7b74bcdcc85374b9203e9a2eb7600712af2236b80ebceb644d5bad65f3ccbcdcf8f2a649a48fd8a3b72023a6a694000be8df5519f7d5e11414b73274377300655be6ad1cc0da6c0eedf1fe36e4e82f3125c8ba40a1bcda444b4ec528d1674e6cee5e61d71f92586212ab2b5afdc90fe4744db5c9873eb240dfaf1952b41b3f3fa7e7a86def2a691bcbae5ad968459f712041a6ea8f39314c19545f8b5b501250aae6d09d0cfff7d9f79f403293e081859a95c05ab7a6fb6b5e960c928a199b89166188d4db5c4546fa8baef03a08004a37d041bcb10ceb94d3797814789ac192c2c79d9ddd41397cb0d0e6af84fcb15cdc885df164aaa81e2dbc95af76d4ca71017b81b2b20307c4a067d844c427f1e5c06da2082911203f61bc44d5983a203861f06e48241d407d56b72bb612792014ff77d8e2288699225241ca75d35823e563f71118851bd5f4f1aebf94c76d380d9f9315a822ef2054c1e234def6087ec7504e7423d51adcf6ed199a317199dbb561ee6ba7ecc736c5cd746fc17fa3609c6b0e1f19c4323d9e77ac67d92fdd82600"
val expectedAcceptJsonString: String = {
s"""
|{
| "result":
| {
| "temporaryContractId": "d1dc7596d2a2f14a2542f2b7e563c149c910ba2238b59532b9fc1ebf6f8465f1",
| "acceptCollateral": 60000,
| "fundingPubkey": "037dbfd3eb9296d1bd537c4008794989893821d48a163b0f3b7b01433599e81211",
| "payoutSpk": "0014cb62b501146622502837f5cd898a657feba867eb",
| "payoutSerialId": "10881229472670123895",
| "fundingInputs": [
| {
| "inputSerialId": "16354653267988371239",
| "prevTx": "02000000000101433df2df0297e1de67283fd8d369a16be5179f3a696fe36bf601337426408af50100000000000000000280f0fa0200000000160014d431e458cc852e7f2f21a6055751b5d2f30b7bcac190f30200000000160014aba424ac1cdf6c4ff53dcc76c1458c2e8bb7c71702473044022049911e914325d2c21f1f7a52d0bef9f50362cf130a87d73a30e61863fa72582502205338e9fa57b6172c821cb9e2530e7b0bf27812e2110ffa7adf277da1be31872d012103cf091ac1820aabeb4399d63bacb5b1c58310166a8b9466bc5efb4dbb678aba1e00000000",
| "prevTxVout": 0,
| "sequence": 4294967295,
| "maxWitnessLen": 107,
| "redeemScript": ""
| }
| ],
| "changeSpk": "00145097a3a31e88036a7396728b6e3ea47c70780f8d",
| "changeSerialId": "13154619712848351014",
| "cetAdaptorSignatures": {
| "ecdsaAdaptorSignatures": [
| {
| "signature": "02834a87e2019a7c431db58235abd00ce62ca5a36e8b5e60bc6dd895045e9d936302e00d2ff673cf117f8bc3e6cbbbdc9efeb6b641d55c32c58ca46422c7279db2a3d07fa999360ba675ad6d342d21997e8644f60509a106df667f85f948f1d73ca5de082db23a50f07d9180e253bd8879567d1e4110f07de6f57ef75cf32206edc4d0656f6dcd738f0301d4baf73182aac102a1917b9688dcb7b74bcdcc85374b92"
| },
| {
| "signature": "03e9a2eb7600712af2236b80ebceb644d5bad65f3ccbcdcf8f2a649a48fd8a3b72023a6a694000be8df5519f7d5e11414b73274377300655be6ad1cc0da6c0eedf1fe36e4e82f3125c8ba40a1bcda444b4ec528d1674e6cee5e61d71f92586212ab2b5afdc90fe4744db5c9873eb240dfaf1952b41b3f3fa7e7a86def2a691bcbae5ad968459f712041a6ea8f39314c19545f8b5b501250aae6d09d0cfff7d9f79f4"
| },
| {
| "signature": "03293e081859a95c05ab7a6fb6b5e960c928a199b89166188d4db5c4546fa8baef03a08004a37d041bcb10ceb94d3797814789ac192c2c79d9ddd41397cb0d0e6af84fcb15cdc885df164aaa81e2dbc95af76d4ca71017b81b2b20307c4a067d844c427f1e5c06da2082911203f61bc44d5983a203861f06e48241d407d56b72bb612792014ff77d8e2288699225241ca75d35823e563f71118851bd5f4f1aebf94c"
| }
| ]
| },
| "refundSignature": "3044022076d380d9f9315a822ef2054c1e234def6087ec7504e7423d51adcf6ed199a3170220199dbb561ee6ba7ecc736c5cd746fc17fa3609c6b0e1f19c4323d9e77ac67d92",
| "negotiationFields": null
| },
|
| "error": null
|}
|""".stripMargin
}
val expectedAccept: ujson.Value = ujson.read(expectedAcceptJsonString)
val signHex =
"a71eafcd7e786c0b9085784a6d063c7f4e7291784f9be3ba23cd8734559040fd2202fda716fd02890402feee4c75948830549fd19efd93cd4e00d4f538041ecf2f2cb6820c78c0a57fc9034d9ce5849e0abe235b8c3137506745508aa17c7a3064ec4172e76c88e66ca27257c5ec09f99201c9bc95b8ab345aff9b32b89c328d21a6ca5553f287a650320162381ef95042d9ac57f66f39dc6904f17a472671d3828f75ef29fbddcd31119b69adb216dac9b5960b91b3e3d83b9310484ef667d19f4bed01964ff063a2acbd025e840169038f0d046cc5005d9d622fd19ec2dd84d15a022f3eff4a781c6f404602cf8ec975191b73ecc3944786f5cca2a01ba3e71bbde9ea6932083c23591dfb6e3e06489cde6415c39cc9e187283f7a9b76227b157572dc62f1a5b130b4fc6b68c14ea07deddb2865d634a244839a87f158145c59d9aecb51b082c3a2ef0cb0201bc65252a3876f7253855ce4d3e72cd650bc275dd878f920162af74865b0346b03ea132cd20738f1a6640f98dc5664d12952e000feb69bbb3c4929c9054b3ca965030b788e83098f906cd3b088ad297e8149d08eb02442f42bd2b9e4455d5a19a96fd329625e1f1d0db9b9f625a6242f1fc53f56ec8090575c704e364518bdf7d690386d0f4a7bfea6c263c5fbc91631b0f0323a92c51f43f3228c9649c5441b338f2e37cbad0d10ce0d80429e2a6a528e4a0e9700188b86c267246b5049ce7da03102c2ec44af74653bdad7db89a38ab61d3dabf0646acdfab1bd5906d4a75f8d7a0c03051281626d4c8c84a5b7d09717c69f1c5340300be5ef6d94a4efe4d1baebbf820f8891a3b86d0f4a9a42d3fa0a44f7d711a5e65cc887b0424530e9b7e49f8d3a21d89762d535c7f121bc057b01b67372ea19762fad714580767ea6a091771808c3806b273382e0792573f624a90e15004a3b852e627f422c07209fd876c3816905d4dcc449db1a5997b82f2aabaffb3414ad1e10c159fc3fcbd6dab121a3cb9837583d39f66aa347a8170dba12cf88102997f460bb27d70365f7f85cd4ce5ee2fda71870000100020047304402204bbe45fd65402ad89784062c2af0f0c88a55f1de4509e4491f7933ada30fbab102207144fe9b8d621d2bef5c226d6f2f8ff43548f4c0bb9ea221a0c2a034a069c74f01002103bdb496d31a30d8e87ab7767fd26bfdfa7f6d06a0cfaea082a91af1ae1814f251"
val expectedSignJsonString: String = {
s"""
|{
| "result":
|
|{
| "contractId": "afcd7e786c0b9085784a6d063c7f4e7291784f9be3ba23cd8734559040fd2202",
| "cetAdaptorSignatures": {
| "ecdsaAdaptorSignatures": [
| {
| "signature": "02feee4c75948830549fd19efd93cd4e00d4f538041ecf2f2cb6820c78c0a57fc9034d9ce5849e0abe235b8c3137506745508aa17c7a3064ec4172e76c88e66ca27257c5ec09f99201c9bc95b8ab345aff9b32b89c328d21a6ca5553f287a650320162381ef95042d9ac57f66f39dc6904f17a472671d3828f75ef29fbddcd31119b69adb216dac9b5960b91b3e3d83b9310484ef667d19f4bed01964ff063a2acbd"
| },
| {
| "signature": "025e840169038f0d046cc5005d9d622fd19ec2dd84d15a022f3eff4a781c6f404602cf8ec975191b73ecc3944786f5cca2a01ba3e71bbde9ea6932083c23591dfb6e3e06489cde6415c39cc9e187283f7a9b76227b157572dc62f1a5b130b4fc6b68c14ea07deddb2865d634a244839a87f158145c59d9aecb51b082c3a2ef0cb0201bc65252a3876f7253855ce4d3e72cd650bc275dd878f920162af74865b0346b"
| },
| {
| "signature": "03ea132cd20738f1a6640f98dc5664d12952e000feb69bbb3c4929c9054b3ca965030b788e83098f906cd3b088ad297e8149d08eb02442f42bd2b9e4455d5a19a96fd329625e1f1d0db9b9f625a6242f1fc53f56ec8090575c704e364518bdf7d690386d0f4a7bfea6c263c5fbc91631b0f0323a92c51f43f3228c9649c5441b338f2e37cbad0d10ce0d80429e2a6a528e4a0e9700188b86c267246b5049ce7da031"
| },
| {
| "signature": "02c2ec44af74653bdad7db89a38ab61d3dabf0646acdfab1bd5906d4a75f8d7a0c03051281626d4c8c84a5b7d09717c69f1c5340300be5ef6d94a4efe4d1baebbf820f8891a3b86d0f4a9a42d3fa0a44f7d711a5e65cc887b0424530e9b7e49f8d3a21d89762d535c7f121bc057b01b67372ea19762fad714580767ea6a091771808c3806b273382e0792573f624a90e15004a3b852e627f422c07209fd876c38169"
| }
| ]
| },
| "refundSignature": "3044022005d4dcc449db1a5997b82f2aabaffb3414ad1e10c159fc3fcbd6dab121a3cb98022037583d39f66aa347a8170dba12cf88102997f460bb27d70365f7f85cd4ce5ee2",
| "fundingSignatures": {
| "fundingSignatures": [
| {
| "witnessElements": [
| {
| "witness": "304402204bbe45fd65402ad89784062c2af0f0c88a55f1de4509e4491f7933ada30fbab102207144fe9b8d621d2bef5c226d6f2f8ff43548f4c0bb9ea221a0c2a034a069c74f01"
| },
| {
| "witness": "03bdb496d31a30d8e87ab7767fd26bfdfa7f6d06a0cfaea082a91af1ae1814f251"
| }
| ]
| }
| ]
| }
| },
|
| "error": null
|}
|""".stripMargin
}
val expectedSign: ujson.Value = ujson.read(expectedSignJsonString)
}