mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-03 18:47:38 +01:00
Pulled down all remaining non-wallet non-gui code on adaptor-dlc (#3101)
This commit is contained in:
parent
a0453ad660
commit
ac3bae403b
27 changed files with 507 additions and 205 deletions
|
@ -5,7 +5,7 @@ import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.{
|
||||||
WalletCreateFundedPsbtOptions
|
WalletCreateFundedPsbtOptions
|
||||||
}
|
}
|
||||||
import org.bitcoins.core.currency.Bitcoins
|
import org.bitcoins.core.currency.Bitcoins
|
||||||
import org.bitcoins.core.number.{UInt32, UInt64}
|
import org.bitcoins.core.number._
|
||||||
import org.bitcoins.core.protocol.BitcoinAddress
|
import org.bitcoins.core.protocol.BitcoinAddress
|
||||||
import org.bitcoins.core.protocol.ln.currency.MilliSatoshis
|
import org.bitcoins.core.protocol.ln.currency.MilliSatoshis
|
||||||
import org.bitcoins.core.protocol.script.{ScriptPubKey, WitnessScriptPubKey}
|
import org.bitcoins.core.protocol.script.{ScriptPubKey, WitnessScriptPubKey}
|
||||||
|
|
|
@ -22,12 +22,7 @@ import org.bitcoins.core.psbt.PSBT
|
||||||
import org.bitcoins.core.util.EnvUtil
|
import org.bitcoins.core.util.EnvUtil
|
||||||
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
|
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
|
||||||
import org.bitcoins.core.wallet.utxo.AddressLabelTag
|
import org.bitcoins.core.wallet.utxo.AddressLabelTag
|
||||||
import org.bitcoins.crypto.{
|
import org.bitcoins.crypto._
|
||||||
AesPassword,
|
|
||||||
DoubleSha256DigestBE,
|
|
||||||
ECPublicKey,
|
|
||||||
Sha256DigestBE
|
|
||||||
}
|
|
||||||
import scodec.bits.ByteVector
|
import scodec.bits.ByteVector
|
||||||
import scopt.OParser
|
import scopt.OParser
|
||||||
import ujson._
|
import ujson._
|
||||||
|
@ -238,7 +233,8 @@ object ConsoleCli {
|
||||||
),
|
),
|
||||||
cmd("acceptdlcofferfromfile")
|
cmd("acceptdlcofferfromfile")
|
||||||
.action((_, conf) =>
|
.action((_, conf) =>
|
||||||
conf.copy(command = AcceptDLCOfferFromFile(new File("").toPath)))
|
conf.copy(command =
|
||||||
|
AcceptDLCOfferFromFile(new File("").toPath, None)))
|
||||||
.text("Accepts a DLC offer given from another party")
|
.text("Accepts a DLC offer given from another party")
|
||||||
.children(
|
.children(
|
||||||
arg[Path]("path")
|
arg[Path]("path")
|
||||||
|
@ -248,6 +244,14 @@ object ConsoleCli {
|
||||||
case accept: AcceptDLCOfferFromFile =>
|
case accept: AcceptDLCOfferFromFile =>
|
||||||
accept.copy(path = path)
|
accept.copy(path = path)
|
||||||
case other => other
|
case other => other
|
||||||
|
})),
|
||||||
|
arg[Path]("destination")
|
||||||
|
.optional()
|
||||||
|
.action((dest, conf) =>
|
||||||
|
conf.copy(command = conf.command match {
|
||||||
|
case accept: AcceptDLCOfferFromFile =>
|
||||||
|
accept.copy(destination = Some(dest))
|
||||||
|
case other => other
|
||||||
}))
|
}))
|
||||||
),
|
),
|
||||||
cmd("signdlc")
|
cmd("signdlc")
|
||||||
|
@ -265,7 +269,7 @@ object ConsoleCli {
|
||||||
),
|
),
|
||||||
cmd("signdlcfromfile")
|
cmd("signdlcfromfile")
|
||||||
.action((_, conf) =>
|
.action((_, conf) =>
|
||||||
conf.copy(command = SignDLCFromFile(new File("").toPath)))
|
conf.copy(command = SignDLCFromFile(new File("").toPath, None)))
|
||||||
.text("Signs a DLC")
|
.text("Signs a DLC")
|
||||||
.children(
|
.children(
|
||||||
arg[Path]("path")
|
arg[Path]("path")
|
||||||
|
@ -275,6 +279,14 @@ object ConsoleCli {
|
||||||
case signDLC: SignDLCFromFile =>
|
case signDLC: SignDLCFromFile =>
|
||||||
signDLC.copy(path = path)
|
signDLC.copy(path = path)
|
||||||
case other => other
|
case other => other
|
||||||
|
})),
|
||||||
|
arg[Path]("destination")
|
||||||
|
.optional()
|
||||||
|
.action((dest, conf) =>
|
||||||
|
conf.copy(command = conf.command match {
|
||||||
|
case accept: SignDLCFromFile =>
|
||||||
|
accept.copy(destination = Some(dest))
|
||||||
|
case other => other
|
||||||
}))
|
}))
|
||||||
),
|
),
|
||||||
cmd("adddlcsigs")
|
cmd("adddlcsigs")
|
||||||
|
@ -383,6 +395,20 @@ object ConsoleCli {
|
||||||
case other => other
|
case other => other
|
||||||
}))
|
}))
|
||||||
),
|
),
|
||||||
|
cmd("canceldlc")
|
||||||
|
.action((_, conf) =>
|
||||||
|
conf.copy(command = CancelDLC(Sha256DigestBE.empty)))
|
||||||
|
.text("Cancels a DLC and unreserves used utxos")
|
||||||
|
.children(
|
||||||
|
arg[Sha256DigestBE]("paramhash")
|
||||||
|
.required()
|
||||||
|
.action((paramHash, conf) =>
|
||||||
|
conf.copy(command = conf.command match {
|
||||||
|
case cancelDLC: CancelDLC =>
|
||||||
|
cancelDLC.copy(paramHash = paramHash)
|
||||||
|
case other => other
|
||||||
|
}))
|
||||||
|
),
|
||||||
cmd("getdlcs")
|
cmd("getdlcs")
|
||||||
.action((_, conf) => conf.copy(command = GetDLCs))
|
.action((_, conf) => conf.copy(command = GetDLCs))
|
||||||
.text("Returns all dlcs in the wallet"),
|
.text("Returns all dlcs in the wallet"),
|
||||||
|
@ -1440,12 +1466,13 @@ object ConsoleCli {
|
||||||
)
|
)
|
||||||
case AcceptDLCOffer(offer) =>
|
case AcceptDLCOffer(offer) =>
|
||||||
RequestParam("acceptdlcoffer", Seq(up.writeJs(offer)))
|
RequestParam("acceptdlcoffer", Seq(up.writeJs(offer)))
|
||||||
case AcceptDLCOfferFromFile(path) =>
|
case AcceptDLCOfferFromFile(path, dest) =>
|
||||||
RequestParam("acceptdlcofferfromfile", Seq(up.writeJs(path)))
|
RequestParam("acceptdlcofferfromfile",
|
||||||
|
Seq(up.writeJs(path), up.writeJs(dest)))
|
||||||
case SignDLC(accept) =>
|
case SignDLC(accept) =>
|
||||||
RequestParam("signdlc", Seq(up.writeJs(accept)))
|
RequestParam("signdlc", Seq(up.writeJs(accept)))
|
||||||
case SignDLCFromFile(path) =>
|
case SignDLCFromFile(path, dest) =>
|
||||||
RequestParam("signdlcfromfile", Seq(up.writeJs(path)))
|
RequestParam("signdlcfromfile", Seq(up.writeJs(path), up.writeJs(dest)))
|
||||||
case AddDLCSigs(sigs) =>
|
case AddDLCSigs(sigs) =>
|
||||||
RequestParam("adddlcsigs", Seq(up.writeJs(sigs)))
|
RequestParam("adddlcsigs", Seq(up.writeJs(sigs)))
|
||||||
case AddDLCSigsFromFile(path) =>
|
case AddDLCSigsFromFile(path) =>
|
||||||
|
@ -1462,6 +1489,8 @@ object ConsoleCli {
|
||||||
case ExecuteDLCRefund(contractId, noBroadcast) =>
|
case ExecuteDLCRefund(contractId, noBroadcast) =>
|
||||||
RequestParam("executedlcrefund",
|
RequestParam("executedlcrefund",
|
||||||
Seq(up.writeJs(contractId), up.writeJs(noBroadcast)))
|
Seq(up.writeJs(contractId), up.writeJs(noBroadcast)))
|
||||||
|
case CancelDLC(paramHash) =>
|
||||||
|
RequestParam("canceldlc", Seq(up.writeJs(paramHash)))
|
||||||
// Wallet
|
// Wallet
|
||||||
case GetBalance(isSats) =>
|
case GetBalance(isSats) =>
|
||||||
RequestParam("getbalance", Seq(up.writeJs(isSats)))
|
RequestParam("getbalance", Seq(up.writeJs(isSats)))
|
||||||
|
@ -1800,13 +1829,15 @@ object CliCommand {
|
||||||
case class AcceptDLCOffer(offer: LnMessage[DLCOfferTLV])
|
case class AcceptDLCOffer(offer: LnMessage[DLCOfferTLV])
|
||||||
extends AcceptDLCCliCommand
|
extends AcceptDLCCliCommand
|
||||||
|
|
||||||
case class AcceptDLCOfferFromFile(path: Path) extends AcceptDLCCliCommand
|
case class AcceptDLCOfferFromFile(path: Path, destination: Option[Path])
|
||||||
|
extends AcceptDLCCliCommand
|
||||||
|
|
||||||
sealed trait SignDLCCliCommand extends AppServerCliCommand
|
sealed trait SignDLCCliCommand extends AppServerCliCommand
|
||||||
|
|
||||||
case class SignDLC(accept: LnMessage[DLCAcceptTLV]) extends SignDLCCliCommand
|
case class SignDLC(accept: LnMessage[DLCAcceptTLV]) extends SignDLCCliCommand
|
||||||
|
|
||||||
case class SignDLCFromFile(path: Path) extends SignDLCCliCommand
|
case class SignDLCFromFile(path: Path, destination: Option[Path])
|
||||||
|
extends SignDLCCliCommand
|
||||||
|
|
||||||
sealed trait AddDLCSigsCliCommand extends AppServerCliCommand
|
sealed trait AddDLCSigsCliCommand extends AppServerCliCommand
|
||||||
|
|
||||||
|
@ -1831,6 +1862,8 @@ object CliCommand {
|
||||||
extends AppServerCliCommand
|
extends AppServerCliCommand
|
||||||
with Broadcastable
|
with Broadcastable
|
||||||
|
|
||||||
|
case class CancelDLC(paramHash: Sha256DigestBE) extends AppServerCliCommand
|
||||||
|
|
||||||
case object GetDLCs extends AppServerCliCommand
|
case object GetDLCs extends AppServerCliCommand
|
||||||
case class GetDLC(paramHash: Sha256DigestBE) extends AppServerCliCommand
|
case class GetDLC(paramHash: Sha256DigestBE) extends AppServerCliCommand
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ class AcceptDLCDialog
|
||||||
// TODO figure how to validate when using a file
|
// TODO figure how to validate when using a file
|
||||||
offerDLCFile = None // reset
|
offerDLCFile = None // reset
|
||||||
offerFileChosenLabel.text = "" // reset
|
offerFileChosenLabel.text = "" // reset
|
||||||
AcceptDLCOfferFromFile(file.toPath)
|
AcceptDLCOfferFromFile(file.toPath, None)
|
||||||
case None =>
|
case None =>
|
||||||
val offerHex = readStringFromNode(inputs(dlcOfferStr))
|
val offerHex = readStringFromNode(inputs(dlcOfferStr))
|
||||||
val offer = LnMessageFactory(DLCOfferTLV).fromHex(offerHex)
|
val offer = LnMessageFactory(DLCOfferTLV).fromHex(offerHex)
|
||||||
|
|
|
@ -29,7 +29,7 @@ class SignDLCDialog
|
||||||
case Some(file) =>
|
case Some(file) =>
|
||||||
acceptDLCFile = None // reset
|
acceptDLCFile = None // reset
|
||||||
acceptFileChosenLabel.text = "" // reset
|
acceptFileChosenLabel.text = "" // reset
|
||||||
SignDLCFromFile(file.toPath)
|
SignDLCFromFile(file.toPath, None)
|
||||||
case None =>
|
case None =>
|
||||||
val acceptHex = readStringFromNode(inputs(dlcAcceptStr))
|
val acceptHex = readStringFromNode(inputs(dlcAcceptStr))
|
||||||
|
|
||||||
|
|
|
@ -4,12 +4,17 @@ import org.bitcoins.core.currency.Satoshis
|
||||||
import org.bitcoins.core.number.{UInt32, UInt64}
|
import org.bitcoins.core.number.{UInt32, UInt64}
|
||||||
import org.bitcoins.core.protocol.BitcoinAddress
|
import org.bitcoins.core.protocol.BitcoinAddress
|
||||||
import org.bitcoins.core.protocol.BlockStamp.{BlockHeight, BlockTime}
|
import org.bitcoins.core.protocol.BlockStamp.{BlockHeight, BlockTime}
|
||||||
import org.bitcoins.core.protocol.dlc.models.DLCMessage.{DLCAccept, DLCOffer}
|
import org.bitcoins.core.protocol.dlc.models.DLCMessage.{
|
||||||
|
DLCAccept,
|
||||||
|
DLCOffer,
|
||||||
|
DLCSign
|
||||||
|
}
|
||||||
import org.bitcoins.core.protocol.dlc.models._
|
import org.bitcoins.core.protocol.dlc.models._
|
||||||
import org.bitcoins.core.protocol.tlv.EnumOutcome
|
import org.bitcoins.core.protocol.tlv.EnumOutcome
|
||||||
import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
|
import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
|
||||||
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
|
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
|
||||||
import org.bitcoins.crypto._
|
import org.bitcoins.crypto._
|
||||||
|
import org.bitcoins.testkitcore.gen.{LnMessageGen, TLVGen}
|
||||||
import org.bitcoins.testkitcore.util.BitcoinSJvmTest
|
import org.bitcoins.testkitcore.util.BitcoinSJvmTest
|
||||||
|
|
||||||
class DLCMessageTest extends BitcoinSJvmTest {
|
class DLCMessageTest extends BitcoinSJvmTest {
|
||||||
|
@ -80,14 +85,41 @@ class DLCMessageTest extends BitcoinSJvmTest {
|
||||||
dummyAddress,
|
dummyAddress,
|
||||||
payoutSerialId = UInt64.zero,
|
payoutSerialId = UInt64.zero,
|
||||||
changeSerialId = UInt64.one,
|
changeSerialId = UInt64.one,
|
||||||
CETSignatures(Vector(
|
CETSignatures(
|
||||||
|
Vector(
|
||||||
EnumOracleOutcome(
|
EnumOracleOutcome(
|
||||||
Vector(dummyOracle),
|
Vector(dummyOracle),
|
||||||
EnumOutcome(dummyStr)) -> ECAdaptorSignature.dummy),
|
EnumOutcome(dummyStr)).sigPoint -> ECAdaptorSignature.dummy),
|
||||||
dummySig),
|
dummySig),
|
||||||
DLCAccept.NoNegotiationFields,
|
DLCAccept.NoNegotiationFields,
|
||||||
Sha256Digest.empty
|
Sha256Digest.empty
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it must "be able to go back and forth between TLV and deserialized" in {
|
||||||
|
forAll(TLVGen.dlcOfferTLVAcceptTLVSignTLV) {
|
||||||
|
case (offerTLV, acceptTLV, signTLV) =>
|
||||||
|
val offer = DLCOffer.fromTLV(offerTLV)
|
||||||
|
val accept = DLCAccept.fromTLV(acceptTLV, offer)
|
||||||
|
val sign = DLCSign.fromTLV(signTLV, offer)
|
||||||
|
|
||||||
|
assert(offer.toTLV == offerTLV)
|
||||||
|
assert(accept.toTLV == acceptTLV)
|
||||||
|
assert(sign.toTLV == signTLV)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it must "be able to go back and forth between LN Message and deserialized" in {
|
||||||
|
forAll(LnMessageGen.dlcOfferMessageAcceptMessageSignMessage) {
|
||||||
|
case (offerMsg, acceptMsg, signMsg) =>
|
||||||
|
val offer = DLCOffer.fromMessage(offerMsg)
|
||||||
|
val accept = DLCAccept.fromMessage(acceptMsg, offer)
|
||||||
|
val sign = DLCSign.fromMessage(signMsg, offer)
|
||||||
|
|
||||||
|
assert(offer.toMessage == offerMsg)
|
||||||
|
assert(accept.toMessage == acceptMsg)
|
||||||
|
assert(sign.toMessage == signMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import org.bitcoins.core.protocol.dlc.models._
|
||||||
import org.bitcoins.core.protocol.script._
|
import org.bitcoins.core.protocol.script._
|
||||||
import org.bitcoins.core.protocol.transaction._
|
import org.bitcoins.core.protocol.transaction._
|
||||||
import org.bitcoins.core.protocol.{BitcoinAddress, BlockTimeStamp}
|
import org.bitcoins.core.protocol.{BitcoinAddress, BlockTimeStamp}
|
||||||
|
import org.bitcoins.core.util.Indexed
|
||||||
import org.bitcoins.core.wallet.builder.DualFundingTxFinalizer
|
import org.bitcoins.core.wallet.builder.DualFundingTxFinalizer
|
||||||
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
|
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
|
||||||
import org.bitcoins.core.wallet.utxo.{
|
import org.bitcoins.core.wallet.utxo.{
|
||||||
|
@ -172,14 +173,19 @@ case class DLCTxBuilder(offer: DLCOffer, accept: DLCAcceptWithoutSigs) {
|
||||||
/** Constructs the unsigned Contract Execution Transaction (CET)
|
/** Constructs the unsigned Contract Execution Transaction (CET)
|
||||||
* for a given outcome hash
|
* for a given outcome hash
|
||||||
*/
|
*/
|
||||||
def buildCET(msg: OracleOutcome): WitnessTransaction = {
|
def buildCET(adaptorPoint: Indexed[ECPublicKey]): WitnessTransaction = {
|
||||||
buildCETs(Vector(msg)).head
|
buildCETs(Vector(adaptorPoint)).head
|
||||||
}
|
}
|
||||||
|
|
||||||
def buildCETsMap(msgs: Vector[OracleOutcome]): Vector[OutcomeCETPair] = {
|
def buildCET(adaptorPoint: ECPublicKey, index: Int): WitnessTransaction = {
|
||||||
|
buildCET(Indexed(adaptorPoint, index))
|
||||||
|
}
|
||||||
|
|
||||||
|
def buildCETsMap(adaptorPoints: Vector[Indexed[ECPublicKey]]): Vector[
|
||||||
|
AdaptorPointCETPair] = {
|
||||||
DLCTxBuilder
|
DLCTxBuilder
|
||||||
.buildCETs(
|
.buildCETs(
|
||||||
msgs,
|
adaptorPoints,
|
||||||
contractInfo,
|
contractInfo,
|
||||||
offerFundingKey,
|
offerFundingKey,
|
||||||
offerFinalAddress.scriptPubKey,
|
offerFinalAddress.scriptPubKey,
|
||||||
|
@ -193,8 +199,9 @@ case class DLCTxBuilder(offer: DLCOffer, accept: DLCAcceptWithoutSigs) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def buildCETs(msgs: Vector[OracleOutcome]): Vector[WitnessTransaction] = {
|
def buildCETs(adaptorPoints: Vector[Indexed[ECPublicKey]]): Vector[
|
||||||
buildCETsMap(msgs).map(_.wtx)
|
WitnessTransaction] = {
|
||||||
|
buildCETsMap(adaptorPoints).map(_.wtx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Constructs the unsigned refund transaction */
|
/** Constructs the unsigned refund transaction */
|
||||||
|
@ -318,7 +325,7 @@ object DLCTxBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
def buildCET(
|
def buildCET(
|
||||||
outcome: OracleOutcome,
|
adaptorPoint: Indexed[ECPublicKey],
|
||||||
contractInfo: ContractInfo,
|
contractInfo: ContractInfo,
|
||||||
offerFundingKey: ECPublicKey,
|
offerFundingKey: ECPublicKey,
|
||||||
offerFinalSPK: ScriptPubKey,
|
offerFinalSPK: ScriptPubKey,
|
||||||
|
@ -328,7 +335,8 @@ object DLCTxBuilder {
|
||||||
acceptSerialId: UInt64,
|
acceptSerialId: UInt64,
|
||||||
timeouts: DLCTimeouts,
|
timeouts: DLCTimeouts,
|
||||||
fundingOutputRef: OutputReference): WitnessTransaction = {
|
fundingOutputRef: OutputReference): WitnessTransaction = {
|
||||||
val Vector(OutcomeCETPair(_, cet)) = buildCETs(Vector(outcome),
|
val Vector(AdaptorPointCETPair(_, cet)) = buildCETs(
|
||||||
|
Vector(adaptorPoint),
|
||||||
contractInfo,
|
contractInfo,
|
||||||
offerFundingKey,
|
offerFundingKey,
|
||||||
offerFinalSPK,
|
offerFinalSPK,
|
||||||
|
@ -337,13 +345,14 @@ object DLCTxBuilder {
|
||||||
acceptFinalSPK,
|
acceptFinalSPK,
|
||||||
acceptSerialId,
|
acceptSerialId,
|
||||||
timeouts,
|
timeouts,
|
||||||
fundingOutputRef)
|
fundingOutputRef
|
||||||
|
)
|
||||||
|
|
||||||
cet
|
cet
|
||||||
}
|
}
|
||||||
|
|
||||||
def buildCETs(
|
def buildCETs(
|
||||||
outcomes: Vector[OracleOutcome],
|
adaptorPoints: Vector[Indexed[ECPublicKey]],
|
||||||
contractInfo: ContractInfo,
|
contractInfo: ContractInfo,
|
||||||
offerFundingKey: ECPublicKey,
|
offerFundingKey: ECPublicKey,
|
||||||
offerFinalSPK: ScriptPubKey,
|
offerFinalSPK: ScriptPubKey,
|
||||||
|
@ -352,7 +361,7 @@ object DLCTxBuilder {
|
||||||
acceptFinalSPK: ScriptPubKey,
|
acceptFinalSPK: ScriptPubKey,
|
||||||
acceptSerialId: UInt64,
|
acceptSerialId: UInt64,
|
||||||
timeouts: DLCTimeouts,
|
timeouts: DLCTimeouts,
|
||||||
fundingOutputRef: OutputReference): Vector[OutcomeCETPair] = {
|
fundingOutputRef: OutputReference): Vector[AdaptorPointCETPair] = {
|
||||||
val builder =
|
val builder =
|
||||||
DLCCETBuilder(contractInfo,
|
DLCCETBuilder(contractInfo,
|
||||||
offerFundingKey,
|
offerFundingKey,
|
||||||
|
@ -364,13 +373,17 @@ object DLCTxBuilder {
|
||||||
timeouts,
|
timeouts,
|
||||||
fundingOutputRef)
|
fundingOutputRef)
|
||||||
|
|
||||||
outcomes.map { outcome =>
|
val outcomes = adaptorPoints.map { case Indexed(_, index) =>
|
||||||
OutcomeCETPair(outcome, builder.buildCET(outcome))
|
contractInfo.allOutcomes(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
adaptorPoints.zip(outcomes).map { case (Indexed(sigPoint, _), outcome) =>
|
||||||
|
AdaptorPointCETPair(sigPoint, builder.buildCET(outcome))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def buildCETs(
|
def buildCETs(
|
||||||
outcomes: Vector[OracleOutcome],
|
adaptorPoints: Vector[Indexed[ECPublicKey]],
|
||||||
contractInfo: ContractInfo,
|
contractInfo: ContractInfo,
|
||||||
offerFundingKey: ECPublicKey,
|
offerFundingKey: ECPublicKey,
|
||||||
offerFinalSPK: ScriptPubKey,
|
offerFinalSPK: ScriptPubKey,
|
||||||
|
@ -380,13 +393,13 @@ object DLCTxBuilder {
|
||||||
acceptSerialId: UInt64,
|
acceptSerialId: UInt64,
|
||||||
timeouts: DLCTimeouts,
|
timeouts: DLCTimeouts,
|
||||||
fundingTx: Transaction,
|
fundingTx: Transaction,
|
||||||
fundOutputIndex: Int): Vector[OutcomeCETPair] = {
|
fundOutputIndex: Int): Vector[AdaptorPointCETPair] = {
|
||||||
val fundingOutPoint =
|
val fundingOutPoint =
|
||||||
TransactionOutPoint(fundingTx.txId, UInt32(fundOutputIndex))
|
TransactionOutPoint(fundingTx.txId, UInt32(fundOutputIndex))
|
||||||
val fundingOutputRef =
|
val fundingOutputRef =
|
||||||
OutputReference(fundingOutPoint, fundingTx.outputs(fundOutputIndex))
|
OutputReference(fundingOutPoint, fundingTx.outputs(fundOutputIndex))
|
||||||
|
|
||||||
buildCETs(outcomes,
|
buildCETs(adaptorPoints,
|
||||||
contractInfo,
|
contractInfo,
|
||||||
offerFundingKey,
|
offerFundingKey,
|
||||||
offerFinalSPK,
|
offerFinalSPK,
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
package org.bitcoins.core.protocol.dlc.compute
|
||||||
|
|
||||||
|
import org.bitcoins.core.protocol.dlc.models.{
|
||||||
|
ContractInfo,
|
||||||
|
EnumContractDescriptor,
|
||||||
|
NumericContractDescriptor
|
||||||
|
}
|
||||||
|
import org.bitcoins.core.protocol.tlv.{
|
||||||
|
EnumOutcome,
|
||||||
|
SignedNumericOutcome,
|
||||||
|
UnsignedNumericOutcome
|
||||||
|
}
|
||||||
|
import org.bitcoins.crypto.{
|
||||||
|
CryptoUtil,
|
||||||
|
ECPublicKey,
|
||||||
|
FieldElement,
|
||||||
|
SchnorrPublicKey
|
||||||
|
}
|
||||||
|
import scodec.bits.ByteVector
|
||||||
|
|
||||||
|
/** Responsible for optimized computation of DLC adaptor point batches. */
|
||||||
|
object DLCAdaptorPointComputer {
|
||||||
|
|
||||||
|
private val base: Int = 2
|
||||||
|
|
||||||
|
private lazy val numericPossibleOutcomes: Vector[ByteVector] = {
|
||||||
|
0
|
||||||
|
.until(base)
|
||||||
|
.toVector
|
||||||
|
.map(_.toString)
|
||||||
|
.map(CryptoUtil.serializeForHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Computes:
|
||||||
|
* nonce + outcomeHash*pubKey
|
||||||
|
* where outcomeHash is as specified in the DLC spec.
|
||||||
|
* @see https://github.com/discreetlogcontracts/dlcspecs/blob/master/Oracle.md#signing-algorithm
|
||||||
|
*/
|
||||||
|
def computePoint(
|
||||||
|
pubKey: SchnorrPublicKey,
|
||||||
|
nonce: ECPublicKey,
|
||||||
|
outcome: ByteVector): ECPublicKey = {
|
||||||
|
val hash = CryptoUtil
|
||||||
|
.sha256SchnorrChallenge(
|
||||||
|
nonce.schnorrNonce.bytes ++ pubKey.bytes ++ CryptoUtil
|
||||||
|
.sha256DLCAttestation(outcome)
|
||||||
|
.bytes)
|
||||||
|
.bytes
|
||||||
|
|
||||||
|
nonce.add(pubKey.publicKey.tweakMultiply(FieldElement(hash)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Efficiently computes all adaptor points, in order, for a given ContractInfo.
|
||||||
|
* @see https://medium.com/crypto-garage/optimizing-numeric-outcome-dlc-creation-6d6091ac0e47
|
||||||
|
*/
|
||||||
|
def computeAdaptorPoints(contractInfo: ContractInfo): Vector[ECPublicKey] = {
|
||||||
|
// The possible messages a single nonce may be used to sign
|
||||||
|
val possibleOutcomes: Vector[ByteVector] =
|
||||||
|
contractInfo.contractDescriptor match {
|
||||||
|
case enum: EnumContractDescriptor =>
|
||||||
|
enum.keys.map(_.outcome).map(CryptoUtil.serializeForHash)
|
||||||
|
case _: NumericContractDescriptor => numericPossibleOutcomes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Oracle -> Nonce -> Outcome -> SubSigPoint
|
||||||
|
// These are the points that are then combined to construct aggregate points.
|
||||||
|
val preComputeTable: Vector[Vector[Vector[ECPublicKey]]] =
|
||||||
|
contractInfo.oracleInfo.singleOracleInfos.map { info =>
|
||||||
|
val announcement = info.announcement
|
||||||
|
val pubKey = announcement.publicKey
|
||||||
|
val nonces = announcement.eventTLV.nonces.map(_.publicKey)
|
||||||
|
|
||||||
|
nonces.map { nonce =>
|
||||||
|
possibleOutcomes.map { outcome =>
|
||||||
|
computePoint(pubKey, nonce, outcome)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val oraclesAndOutcomes = contractInfo.allOutcomes.map(_.oraclesAndOutcomes)
|
||||||
|
|
||||||
|
oraclesAndOutcomes.map { oracleAndOutcome =>
|
||||||
|
// For the given oracleAndOutcome, look up the point in the preComputeTable
|
||||||
|
val subSigPoints = oracleAndOutcome.flatMap { case (info, outcome) =>
|
||||||
|
val oracleIndex =
|
||||||
|
contractInfo.oracleInfo.singleOracleInfos.indexOf(info)
|
||||||
|
val outcomeIndices = outcome match {
|
||||||
|
case outcome: EnumOutcome =>
|
||||||
|
Vector(
|
||||||
|
contractInfo.contractDescriptor
|
||||||
|
.asInstanceOf[EnumContractDescriptor]
|
||||||
|
.keys
|
||||||
|
.indexOf(outcome)
|
||||||
|
)
|
||||||
|
case UnsignedNumericOutcome(digits) => digits
|
||||||
|
case _: SignedNumericOutcome =>
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Signed numeric outcomes not supported!")
|
||||||
|
}
|
||||||
|
|
||||||
|
outcomeIndices.zipWithIndex.map { case (outcomeIndex, nonceIndex) =>
|
||||||
|
preComputeTable(oracleIndex)(nonceIndex)(outcomeIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Memoization of sub-combinations for further optimization!
|
||||||
|
CryptoUtil.combinePubKeys(subSigPoints)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,43 +24,38 @@ object DLCUtil {
|
||||||
* This method is used to search through possible cetSigs until the correct
|
* This method is used to search through possible cetSigs until the correct
|
||||||
* one is found by validating the returned signature.
|
* one is found by validating the returned signature.
|
||||||
*
|
*
|
||||||
* @param outcome A potential outcome that could have been executed for
|
* @param adaptorPoint A potential adaptor point that could have been executed for
|
||||||
* @param adaptorSig The adaptor signature corresponding to outcome
|
* @param adaptorSig The adaptor signature corresponding to outcome
|
||||||
* @param cetSig The actual signature for local's key found on-chain on a CET
|
* @param cetSig The actual signature for local's key found on-chain on a CET
|
||||||
*/
|
*/
|
||||||
private def sigFromOutcomeAndSigs(
|
private def sigFromOutcomeAndSigs(
|
||||||
outcome: OracleOutcome,
|
adaptorPoint: ECPublicKey,
|
||||||
adaptorSig: ECAdaptorSignature,
|
adaptorSig: ECAdaptorSignature,
|
||||||
cetSig: ECDigitalSignature): Try[SchnorrDigitalSignature] = {
|
cetSig: ECDigitalSignature): Try[FieldElement] = {
|
||||||
val sigPubKey = outcome.sigPoint
|
|
||||||
|
|
||||||
// This value is either the oracle signature S value or it is
|
// This value is either the oracle signature S value or it is
|
||||||
// useless garbage, but we don't know in this scope, the caller
|
// useless garbage, but we don't know in this scope, the caller
|
||||||
// must do further work to check this.
|
// must do further work to check this.
|
||||||
val possibleOracleST = Try {
|
Try {
|
||||||
sigPubKey
|
adaptorPoint
|
||||||
.extractAdaptorSecret(adaptorSig, ECDigitalSignature(cetSig.bytes.init))
|
.extractAdaptorSecret(adaptorSig, ECDigitalSignature(cetSig.bytes.init))
|
||||||
.fieldElement
|
.fieldElement
|
||||||
}
|
}
|
||||||
|
|
||||||
possibleOracleST.map { possibleOracleS =>
|
|
||||||
SchnorrDigitalSignature(outcome.aggregateNonce, possibleOracleS)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def computeOutcome(
|
def computeOutcome(
|
||||||
completedSig: ECDigitalSignature,
|
completedSig: ECDigitalSignature,
|
||||||
possibleAdaptorSigs: Vector[(OracleOutcome, ECAdaptorSignature)]): Option[
|
possibleAdaptorSigs: Vector[(ECPublicKey, ECAdaptorSignature)]): Option[
|
||||||
(SchnorrDigitalSignature, OracleOutcome)] = {
|
(FieldElement, ECPublicKey)] = {
|
||||||
val sigOpt = possibleAdaptorSigs.find { case (outcome, adaptorSig) =>
|
val sigOpt = possibleAdaptorSigs.find { case (adaptorPoint, adaptorSig) =>
|
||||||
val possibleOracleSigT =
|
val possibleOracleSigT =
|
||||||
sigFromOutcomeAndSigs(outcome, adaptorSig, completedSig)
|
sigFromOutcomeAndSigs(adaptorPoint, adaptorSig, completedSig)
|
||||||
|
|
||||||
possibleOracleSigT.isSuccess && possibleOracleSigT.get.sig.getPublicKey == outcome.sigPoint
|
possibleOracleSigT.isSuccess && possibleOracleSigT.get.getPublicKey == adaptorPoint
|
||||||
}
|
}
|
||||||
|
|
||||||
sigOpt.map { case (outcome, adaptorSig) =>
|
sigOpt.map { case (adaptorPoint, adaptorSig) =>
|
||||||
(sigFromOutcomeAndSigs(outcome, adaptorSig, completedSig).get, outcome)
|
(sigFromOutcomeAndSigs(adaptorPoint, adaptorSig, completedSig).get,
|
||||||
|
adaptorPoint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,9 +64,11 @@ object DLCUtil {
|
||||||
offerFundingKey: ECPublicKey,
|
offerFundingKey: ECPublicKey,
|
||||||
acceptFundingKey: ECPublicKey,
|
acceptFundingKey: ECPublicKey,
|
||||||
contractInfo: ContractInfo,
|
contractInfo: ContractInfo,
|
||||||
localAdaptorSigs: Vector[(OracleOutcome, ECAdaptorSignature)],
|
localAdaptorSigs: Vector[(ECPublicKey, ECAdaptorSignature)],
|
||||||
cet: WitnessTransaction): Option[
|
cet: WitnessTransaction): Option[
|
||||||
(SchnorrDigitalSignature, OracleOutcome)] = {
|
(SchnorrDigitalSignature, OracleOutcome)] = {
|
||||||
|
val allAdaptorPoints = contractInfo.adaptorPoints
|
||||||
|
|
||||||
val cetSigs = cet.witness.head
|
val cetSigs = cet.witness.head
|
||||||
.asInstanceOf[P2WSHWitnessV0]
|
.asInstanceOf[P2WSHWitnessV0]
|
||||||
.signatures
|
.signatures
|
||||||
|
@ -82,8 +79,8 @@ object DLCUtil {
|
||||||
val outcomeValues = cet.outputs.map(_.value).sorted
|
val outcomeValues = cet.outputs.map(_.value).sorted
|
||||||
val totalCollateral = contractInfo.totalCollateral
|
val totalCollateral = contractInfo.totalCollateral
|
||||||
|
|
||||||
val possibleOutcomes = contractInfo.allOutcomesAndPayouts
|
val possibleOutcomes = contractInfo.allOutcomesAndPayouts.zipWithIndex
|
||||||
.filter { case (_, amt) =>
|
.filter { case ((_, amt), _) =>
|
||||||
val amts = Vector(amt, totalCollateral - amt)
|
val amts = Vector(amt, totalCollateral - amt)
|
||||||
.filter(_ >= Policy.dustThreshold)
|
.filter(_ >= Policy.dustThreshold)
|
||||||
.sorted
|
.sorted
|
||||||
|
@ -95,7 +92,7 @@ object DLCUtil {
|
||||||
Math.abs((amts.head - outcomeValues.head).satoshis.toLong) <= 1 && Math
|
Math.abs((amts.head - outcomeValues.head).satoshis.toLong) <= 1 && Math
|
||||||
.abs((amts.last - outcomeValues.last).satoshis.toLong) <= 1
|
.abs((amts.last - outcomeValues.last).satoshis.toLong) <= 1
|
||||||
}
|
}
|
||||||
.map(_._1)
|
.map { case (_, index) => allAdaptorPoints(index) }
|
||||||
|
|
||||||
val (offerCETSig, acceptCETSig) =
|
val (offerCETSig, acceptCETSig) =
|
||||||
if (offerFundingKey.hex.compareTo(acceptFundingKey.hex) > 0) {
|
if (offerFundingKey.hex.compareTo(acceptFundingKey.hex) > 0) {
|
||||||
|
@ -114,6 +111,11 @@ object DLCUtil {
|
||||||
offerCETSig
|
offerCETSig
|
||||||
}
|
}
|
||||||
|
|
||||||
computeOutcome(cetSig, outcomeSigs)
|
computeOutcome(cetSig, outcomeSigs).map { case (s, adaptorPoint) =>
|
||||||
|
val index = allAdaptorPoints.indexOf(adaptorPoint)
|
||||||
|
val outcome: OracleOutcome = contractInfo.allOutcomes(index)
|
||||||
|
|
||||||
|
(SchnorrDigitalSignature(outcome.aggregateNonce, s), outcome)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import org.bitcoins.core.protocol.dlc.models._
|
||||||
import org.bitcoins.core.protocol.dlc.sign.DLCTxSigner
|
import org.bitcoins.core.protocol.dlc.sign.DLCTxSigner
|
||||||
import org.bitcoins.core.protocol.transaction.{Transaction, WitnessTransaction}
|
import org.bitcoins.core.protocol.transaction.{Transaction, WitnessTransaction}
|
||||||
import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
|
import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
|
||||||
|
import org.bitcoins.core.util.Indexed
|
||||||
import org.bitcoins.crypto.{AdaptorSign, ECPublicKey}
|
import org.bitcoins.crypto.{AdaptorSign, ECPublicKey}
|
||||||
|
|
||||||
import scala.util.{Success, Try}
|
import scala.util.{Success, Try}
|
||||||
|
@ -51,7 +52,7 @@ case class DLCExecutor(signer: DLCTxSigner) {
|
||||||
}
|
}
|
||||||
|
|
||||||
val CETSignatures(outcomeSigs, refundSig) = cetSigs
|
val CETSignatures(outcomeSigs, refundSig) = cetSigs
|
||||||
val msgs = outcomeSigs.map(_._1)
|
val msgs = Indexed(outcomeSigs.map(_._1))
|
||||||
val cets = cetsOpt match {
|
val cets = cetsOpt match {
|
||||||
case Some(cets) => cets
|
case Some(cets) => cets
|
||||||
case None => builder.buildCETs(msgs)
|
case None => builder.buildCETs(msgs)
|
||||||
|
@ -123,7 +124,7 @@ object DLCExecutor {
|
||||||
* a valid set of expected oracle signatures as per the oracle announcements in the ContractInfo.
|
* a valid set of expected oracle signatures as per the oracle announcements in the ContractInfo.
|
||||||
*/
|
*/
|
||||||
def executeDLC(
|
def executeDLC(
|
||||||
remoteCETInfos: Vector[(OracleOutcome, CETInfo)],
|
remoteCETInfos: Vector[(ECPublicKey, CETInfo)],
|
||||||
oracleSigs: Vector[OracleSignatures],
|
oracleSigs: Vector[OracleSignatures],
|
||||||
fundingKey: AdaptorSign,
|
fundingKey: AdaptorSign,
|
||||||
remoteFundingPubKey: ECPublicKey,
|
remoteFundingPubKey: ECPublicKey,
|
||||||
|
@ -141,7 +142,9 @@ object DLCExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
val msgAndCETInfoOpt = msgOpt.flatMap { msg =>
|
val msgAndCETInfoOpt = msgOpt.flatMap { msg =>
|
||||||
remoteCETInfos.find(_._1 == msg)
|
remoteCETInfos
|
||||||
|
.find(_._1 == msg.sigPoint)
|
||||||
|
.map { case (_, info) => (msg, info) }
|
||||||
}
|
}
|
||||||
|
|
||||||
val (msg, ucet, remoteAdaptorSig) = msgAndCETInfoOpt match {
|
val (msg, ucet, remoteAdaptorSig) = msgAndCETInfoOpt match {
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
package org.bitcoins.core.protocol.dlc.execution
|
package org.bitcoins.core.protocol.dlc.execution
|
||||||
|
|
||||||
import org.bitcoins.core.protocol.dlc.models.OracleOutcome
|
|
||||||
import org.bitcoins.core.protocol.transaction.{Transaction, WitnessTransaction}
|
import org.bitcoins.core.protocol.transaction.{Transaction, WitnessTransaction}
|
||||||
import org.bitcoins.crypto.ECAdaptorSignature
|
import org.bitcoins.crypto.{ECAdaptorSignature, ECPublicKey}
|
||||||
|
|
||||||
case class SetupDLC(
|
case class SetupDLC(
|
||||||
fundingTx: Transaction,
|
fundingTx: Transaction,
|
||||||
cets: Vector[(OracleOutcome, CETInfo)],
|
cets: Vector[(ECPublicKey, CETInfo)],
|
||||||
refundTx: WitnessTransaction) {
|
refundTx: WitnessTransaction) {
|
||||||
cets.foreach { case (msg, cetInfo) =>
|
cets.foreach { case (msg, cetInfo) =>
|
||||||
require(
|
require(
|
||||||
|
@ -25,12 +24,12 @@ case class SetupDLC(
|
||||||
s"RefundTx is not spending the funding tx, ${refundTx.inputs.head}"
|
s"RefundTx is not spending the funding tx, ${refundTx.inputs.head}"
|
||||||
)
|
)
|
||||||
|
|
||||||
def getCETInfo(outcome: OracleOutcome): CETInfo = {
|
def getCETInfo(adaptorPoint: ECPublicKey): CETInfo = {
|
||||||
cets.find(_._1 == outcome) match {
|
cets.find(_._1 == adaptorPoint) match {
|
||||||
case Some((_, info)) => info
|
case Some((_, info)) => info
|
||||||
case None =>
|
case None =>
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
s"No CET found for the given outcome $outcome")
|
s"No CET found for the given adaptor point $adaptorPoint")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,10 @@ import org.bitcoins.core.protocol.dlc.compute.CETCalculator.{
|
||||||
CETOutcome,
|
CETOutcome,
|
||||||
MultiOracleOutcome
|
MultiOracleOutcome
|
||||||
}
|
}
|
||||||
import org.bitcoins.core.protocol.dlc.compute.CETCalculator
|
import org.bitcoins.core.protocol.dlc.compute.{
|
||||||
|
CETCalculator,
|
||||||
|
DLCAdaptorPointComputer
|
||||||
|
}
|
||||||
import org.bitcoins.core.protocol.dlc.models.DLCMessage.DLCAccept
|
import org.bitcoins.core.protocol.dlc.models.DLCMessage.DLCAccept
|
||||||
import org.bitcoins.core.protocol.tlv.{
|
import org.bitcoins.core.protocol.tlv.{
|
||||||
ContractInfoV0TLV,
|
ContractInfoV0TLV,
|
||||||
|
@ -13,6 +16,7 @@ import org.bitcoins.core.protocol.tlv.{
|
||||||
TLVSerializable,
|
TLVSerializable,
|
||||||
UnsignedNumericOutcome
|
UnsignedNumericOutcome
|
||||||
}
|
}
|
||||||
|
import org.bitcoins.core.util.Indexed
|
||||||
import org.bitcoins.crypto.ECPublicKey
|
import org.bitcoins.crypto.ECPublicKey
|
||||||
|
|
||||||
import scala.collection.immutable.HashMap
|
import scala.collection.immutable.HashMap
|
||||||
|
@ -134,16 +138,16 @@ case class ContractInfo(
|
||||||
|
|
||||||
/** Maps adpator points to their corresponding OracleOutcomes (which correspond to CETs) */
|
/** Maps adpator points to their corresponding OracleOutcomes (which correspond to CETs) */
|
||||||
lazy val sigPointMap: Map[ECPublicKey, OracleOutcome] =
|
lazy val sigPointMap: Map[ECPublicKey, OracleOutcome] =
|
||||||
allOutcomes.map(outcome => outcome.sigPoint -> outcome).toMap
|
adaptorPoints.zip(allOutcomes).toMap
|
||||||
|
|
||||||
/** Map OracleOutcomes (which correspond to CETs) to their adpator point and payouts */
|
/** Map OracleOutcomes (which correspond to CETs) to their adpator point and payouts */
|
||||||
lazy val outcomeMap: Map[OracleOutcome, (ECPublicKey, Satoshis, Satoshis)] = {
|
lazy val outcomeMap: Map[OracleOutcome, (ECPublicKey, Satoshis, Satoshis)] = {
|
||||||
val builder =
|
val builder =
|
||||||
HashMap.newBuilder[OracleOutcome, (ECPublicKey, Satoshis, Satoshis)]
|
HashMap.newBuilder[OracleOutcome, (ECPublicKey, Satoshis, Satoshis)]
|
||||||
|
|
||||||
allOutcomesAndPayouts.foreach { case (outcome, offerPayout) =>
|
allOutcomesAndPayouts.zip(adaptorPoints).foreach {
|
||||||
|
case ((outcome, offerPayout), adaptorPoint) =>
|
||||||
val acceptPayout = (totalCollateral - offerPayout).satoshis
|
val acceptPayout = (totalCollateral - offerPayout).satoshis
|
||||||
val adaptorPoint = outcome.sigPoint
|
|
||||||
|
|
||||||
builder.+=((outcome, (adaptorPoint, offerPayout, acceptPayout)))
|
builder.+=((outcome, (adaptorPoint, offerPayout, acceptPayout)))
|
||||||
}
|
}
|
||||||
|
@ -151,6 +155,13 @@ case class ContractInfo(
|
||||||
builder.result()
|
builder.result()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lazy val adaptorPoints: Vector[ECPublicKey] = {
|
||||||
|
DLCAdaptorPointComputer.computeAdaptorPoints(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy val adaptorPointsIndexed: Vector[Indexed[ECPublicKey]] = Indexed(
|
||||||
|
adaptorPoints)
|
||||||
|
|
||||||
/** Checks if the given OracleSignatures exactly match the given OracleOutcome.
|
/** Checks if the given OracleSignatures exactly match the given OracleOutcome.
|
||||||
*
|
*
|
||||||
* Warning: This will return false if too many OracleSignatures are given.
|
* Warning: This will return false if too many OracleSignatures are given.
|
||||||
|
|
|
@ -275,10 +275,10 @@ object DLCMessage {
|
||||||
def fromTLV(
|
def fromTLV(
|
||||||
accept: DLCAcceptTLV,
|
accept: DLCAcceptTLV,
|
||||||
network: NetworkParameters,
|
network: NetworkParameters,
|
||||||
outcomes: Vector[OracleOutcome]): DLCAccept = {
|
adaptorPoints: Vector[ECPublicKey]): DLCAccept = {
|
||||||
val outcomeSigs = accept.cetSignatures match {
|
val outcomeSigs = accept.cetSignatures match {
|
||||||
case CETSignaturesV0TLV(sigs) =>
|
case CETSignaturesV0TLV(sigs) =>
|
||||||
outcomes.zip(sigs)
|
adaptorPoints.zip(sigs)
|
||||||
}
|
}
|
||||||
|
|
||||||
DLCAccept(
|
DLCAccept(
|
||||||
|
@ -308,7 +308,7 @@ object DLCMessage {
|
||||||
accept: DLCAcceptTLV,
|
accept: DLCAcceptTLV,
|
||||||
network: NetworkParameters,
|
network: NetworkParameters,
|
||||||
contractInfo: ContractInfo): DLCAccept = {
|
contractInfo: ContractInfo): DLCAccept = {
|
||||||
fromTLV(accept, network, contractInfo.allOutcomes)
|
fromTLV(accept, network, contractInfo.adaptorPoints)
|
||||||
}
|
}
|
||||||
|
|
||||||
def fromTLV(accept: DLCAcceptTLV, offer: DLCOffer): DLCAccept = {
|
def fromTLV(accept: DLCAcceptTLV, offer: DLCOffer): DLCAccept = {
|
||||||
|
@ -348,11 +348,11 @@ object DLCMessage {
|
||||||
def fromTLV(
|
def fromTLV(
|
||||||
sign: DLCSignTLV,
|
sign: DLCSignTLV,
|
||||||
fundingPubKey: ECPublicKey,
|
fundingPubKey: ECPublicKey,
|
||||||
outcomes: Vector[OracleOutcome],
|
adaptorPoints: Vector[ECPublicKey],
|
||||||
fundingOutPoints: Vector[TransactionOutPoint]): DLCSign = {
|
fundingOutPoints: Vector[TransactionOutPoint]): DLCSign = {
|
||||||
val outcomeSigs = sign.cetSignatures match {
|
val outcomeSigs = sign.cetSignatures match {
|
||||||
case CETSignaturesV0TLV(sigs) =>
|
case CETSignaturesV0TLV(sigs) =>
|
||||||
outcomes.zip(sigs)
|
adaptorPoints.zip(sigs)
|
||||||
}
|
}
|
||||||
|
|
||||||
val sigs = sign.fundingSignatures match {
|
val sigs = sign.fundingSignatures match {
|
||||||
|
@ -376,7 +376,7 @@ object DLCMessage {
|
||||||
def fromTLV(sign: DLCSignTLV, offer: DLCOffer): DLCSign = {
|
def fromTLV(sign: DLCSignTLV, offer: DLCOffer): DLCSign = {
|
||||||
fromTLV(sign,
|
fromTLV(sign,
|
||||||
offer.pubKeys.fundingKey,
|
offer.pubKeys.fundingKey,
|
||||||
offer.contractInfo.allOutcomes,
|
offer.contractInfo.adaptorPoints,
|
||||||
offer.fundingInputs.map(_.outPoint))
|
offer.fundingInputs.map(_.outPoint))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,8 @@ import org.bitcoins.core.protocol.script.ScriptWitnessV0
|
||||||
import org.bitcoins.core.protocol.tlv.FundingSignaturesV0TLV
|
import org.bitcoins.core.protocol.tlv.FundingSignaturesV0TLV
|
||||||
import org.bitcoins.core.protocol.transaction.TransactionOutPoint
|
import org.bitcoins.core.protocol.transaction.TransactionOutPoint
|
||||||
import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
|
import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
|
||||||
import org.bitcoins.core.util.SeqWrapper
|
import org.bitcoins.core.util.{Indexed, SeqWrapper}
|
||||||
import org.bitcoins.crypto.ECAdaptorSignature
|
import org.bitcoins.crypto.{ECAdaptorSignature, ECPublicKey}
|
||||||
|
|
||||||
sealed trait DLCSignatures
|
sealed trait DLCSignatures
|
||||||
|
|
||||||
|
@ -35,13 +35,19 @@ case class FundingSignatures(
|
||||||
}
|
}
|
||||||
|
|
||||||
case class CETSignatures(
|
case class CETSignatures(
|
||||||
outcomeSigs: Vector[(OracleOutcome, ECAdaptorSignature)],
|
outcomeSigs: Vector[(ECPublicKey, ECAdaptorSignature)],
|
||||||
refundSig: PartialSignature)
|
refundSig: PartialSignature)
|
||||||
extends DLCSignatures {
|
extends DLCSignatures {
|
||||||
lazy val keys: Vector[OracleOutcome] = outcomeSigs.map(_._1)
|
lazy val keys: Vector[ECPublicKey] = outcomeSigs.map(_._1)
|
||||||
lazy val adaptorSigs: Vector[ECAdaptorSignature] = outcomeSigs.map(_._2)
|
lazy val adaptorSigs: Vector[ECAdaptorSignature] = outcomeSigs.map(_._2)
|
||||||
|
|
||||||
def apply(key: OracleOutcome): ECAdaptorSignature = {
|
def indexedOutcomeSigs: Vector[(Indexed[ECPublicKey], ECAdaptorSignature)] = {
|
||||||
|
outcomeSigs.zipWithIndex.map { case ((adaptorPoint, sig), index) =>
|
||||||
|
(Indexed(adaptorPoint, index), sig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def apply(key: ECPublicKey): ECAdaptorSignature = {
|
||||||
outcomeSigs
|
outcomeSigs
|
||||||
.find(_._1 == key)
|
.find(_._1 == key)
|
||||||
.map(_._2)
|
.map(_._2)
|
||||||
|
|
|
@ -25,6 +25,8 @@ sealed trait OracleOutcome {
|
||||||
*/
|
*/
|
||||||
def outcome: DLCOutcomeType
|
def outcome: DLCOutcomeType
|
||||||
|
|
||||||
|
def oraclesAndOutcomes: Vector[(SingleOracleInfo, DLCOutcomeType)]
|
||||||
|
|
||||||
protected def computeSigPoint: ECPublicKey
|
protected def computeSigPoint: ECPublicKey
|
||||||
|
|
||||||
/** The adaptor point used to encrypt the signatures for this corresponding CET. */
|
/** The adaptor point used to encrypt the signatures for this corresponding CET. */
|
||||||
|
@ -48,6 +50,9 @@ case class EnumOracleOutcome(
|
||||||
oracles.map(_.sigPoint(outcome)).reduce(_.add(_))
|
oracles.map(_.sigPoint(outcome)).reduce(_.add(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val oraclesAndOutcomes: Vector[(EnumSingleOracleInfo, EnumOutcome)] =
|
||||||
|
oracles.map((_, outcome))
|
||||||
|
|
||||||
override lazy val aggregateNonce: SchnorrNonce = {
|
override lazy val aggregateNonce: SchnorrNonce = {
|
||||||
oracles
|
oracles
|
||||||
.map(_.aggregateNonce(outcome))
|
.map(_.aggregateNonce(outcome))
|
||||||
|
@ -60,7 +65,7 @@ case class EnumOracleOutcome(
|
||||||
/** Corresponds to a CET in an Numeric Outcome DLC where some set of `threshold`
|
/** Corresponds to a CET in an Numeric Outcome DLC where some set of `threshold`
|
||||||
* oracles have each signed some NumericOutcome.
|
* oracles have each signed some NumericOutcome.
|
||||||
*/
|
*/
|
||||||
case class NumericOracleOutcome(oraclesAndOutcomes: Vector[
|
case class NumericOracleOutcome(override val oraclesAndOutcomes: Vector[
|
||||||
(NumericSingleOracleInfo, UnsignedNumericOutcome)])
|
(NumericSingleOracleInfo, UnsignedNumericOutcome)])
|
||||||
extends OracleOutcome {
|
extends OracleOutcome {
|
||||||
|
|
||||||
|
@ -103,5 +108,5 @@ object NumericOracleOutcome {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An oracle outcome and it's corresponding CET */
|
/** An adaptor point and it's corresponding CET */
|
||||||
case class OutcomeCETPair(outcome: OracleOutcome, wtx: WitnessTransaction)
|
case class AdaptorPointCETPair(sigPoint: ECPublicKey, wtx: WitnessTransaction)
|
||||||
|
|
|
@ -15,7 +15,7 @@ import org.bitcoins.core.protocol.{Bech32Address, BitcoinAddress}
|
||||||
import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
|
import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
|
||||||
import org.bitcoins.core.psbt.PSBT
|
import org.bitcoins.core.psbt.PSBT
|
||||||
import org.bitcoins.core.script.crypto.HashType
|
import org.bitcoins.core.script.crypto.HashType
|
||||||
import org.bitcoins.core.util.FutureUtil
|
import org.bitcoins.core.util.{FutureUtil, Indexed}
|
||||||
import org.bitcoins.core.wallet.signer.BitcoinSigner
|
import org.bitcoins.core.wallet.signer.BitcoinSigner
|
||||||
import org.bitcoins.core.wallet.utxo._
|
import org.bitcoins.core.wallet.utxo._
|
||||||
import org.bitcoins.crypto._
|
import org.bitcoins.crypto._
|
||||||
|
@ -125,28 +125,28 @@ case class DLCTxSigner(
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Signs remote's Contract Execution Transaction (CET) for a given outcome */
|
/** Signs remote's Contract Execution Transaction (CET) for a given outcome */
|
||||||
def signCET(outcome: OracleOutcome): ECAdaptorSignature = {
|
def signCET(adaptorPoint: ECPublicKey, index: Int): ECAdaptorSignature = {
|
||||||
signCETs(Vector(outcome)).head._2
|
signCETs(Vector(Indexed(adaptorPoint, index))).head._2
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Signs remote's Contract Execution Transaction (CET) for a given outcomes */
|
/** Signs remote's Contract Execution Transaction (CET) for a given outcomes */
|
||||||
def buildAndSignCETs(outcomes: Vector[OracleOutcome]): Vector[
|
def buildAndSignCETs(adaptorPoints: Vector[Indexed[ECPublicKey]]): Vector[
|
||||||
(OracleOutcome, WitnessTransaction, ECAdaptorSignature)] = {
|
(ECPublicKey, WitnessTransaction, ECAdaptorSignature)] = {
|
||||||
val outcomesAndCETs = builder.buildCETsMap(outcomes)
|
val outcomesAndCETs = builder.buildCETsMap(adaptorPoints)
|
||||||
DLCTxSigner.buildAndSignCETs(outcomesAndCETs, cetSigningInfo, fundingKey)
|
DLCTxSigner.buildAndSignCETs(outcomesAndCETs, cetSigningInfo, fundingKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Signs remote's Contract Execution Transaction (CET) for a given outcomes */
|
/** Signs remote's Contract Execution Transaction (CET) for a given outcomes */
|
||||||
def signCETs(outcomes: Vector[OracleOutcome]): Vector[
|
def signCETs(adaptorPoints: Vector[Indexed[ECPublicKey]]): Vector[
|
||||||
(OracleOutcome, ECAdaptorSignature)] = {
|
(ECPublicKey, ECAdaptorSignature)] = {
|
||||||
buildAndSignCETs(outcomes).map { case (outcome, _, sig) =>
|
buildAndSignCETs(adaptorPoints).map { case (outcome, _, sig) =>
|
||||||
outcome -> sig
|
outcome -> sig
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Signs remote's Contract Execution Transaction (CET) for a given outcomes and their corresponding CETs */
|
/** Signs remote's Contract Execution Transaction (CET) for a given outcomes and their corresponding CETs */
|
||||||
def signGivenCETs(outcomesAndCETs: Vector[OutcomeCETPair]): Vector[
|
def signGivenCETs(outcomesAndCETs: Vector[AdaptorPointCETPair]): Vector[
|
||||||
(OracleOutcome, ECAdaptorSignature)] = {
|
(ECPublicKey, ECAdaptorSignature)] = {
|
||||||
DLCTxSigner.signCETs(outcomesAndCETs, cetSigningInfo, fundingKey)
|
DLCTxSigner.signCETs(outcomesAndCETs, cetSigningInfo, fundingKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,12 +154,14 @@ case class DLCTxSigner(
|
||||||
outcome: OracleOutcome,
|
outcome: OracleOutcome,
|
||||||
remoteAdaptorSig: ECAdaptorSignature,
|
remoteAdaptorSig: ECAdaptorSignature,
|
||||||
oracleSigs: Vector[OracleSignatures]): WitnessTransaction = {
|
oracleSigs: Vector[OracleSignatures]): WitnessTransaction = {
|
||||||
|
val index = builder.contractInfo.allOutcomes.indexOf(outcome)
|
||||||
|
|
||||||
DLCTxSigner.completeCET(
|
DLCTxSigner.completeCET(
|
||||||
outcome,
|
outcome,
|
||||||
cetSigningInfo,
|
cetSigningInfo,
|
||||||
builder.fundingMultiSig,
|
builder.fundingMultiSig,
|
||||||
builder.buildFundingTx,
|
builder.buildFundingTx,
|
||||||
builder.buildCET(outcome),
|
builder.buildCET(outcome.sigPoint, index),
|
||||||
remoteAdaptorSig,
|
remoteAdaptorSig,
|
||||||
remoteFundingPubKey,
|
remoteFundingPubKey,
|
||||||
oracleSigs
|
oracleSigs
|
||||||
|
@ -184,7 +186,8 @@ case class DLCTxSigner(
|
||||||
|
|
||||||
/** Creates all of this party's CETSignatures */
|
/** Creates all of this party's CETSignatures */
|
||||||
def createCETSigs(): CETSignatures = {
|
def createCETSigs(): CETSignatures = {
|
||||||
val cetSigs = signCETs(builder.contractInfo.allOutcomes)
|
val adaptorPoints = builder.contractInfo.adaptorPointsIndexed
|
||||||
|
val cetSigs = signCETs(adaptorPoints)
|
||||||
val refundSig = signRefundTx
|
val refundSig = signRefundTx
|
||||||
|
|
||||||
CETSignatures(cetSigs, refundSig)
|
CETSignatures(cetSigs, refundSig)
|
||||||
|
@ -193,19 +196,26 @@ case class DLCTxSigner(
|
||||||
/** Creates CET signatures async */
|
/** Creates CET signatures async */
|
||||||
def createCETSigsAsync()(implicit
|
def createCETSigsAsync()(implicit
|
||||||
ec: ExecutionContext): Future[CETSignatures] = {
|
ec: ExecutionContext): Future[CETSignatures] = {
|
||||||
val outcomes = builder.contractInfo.allOutcomes
|
val adaptorPoints = builder.contractInfo.adaptorPointsIndexed
|
||||||
|
//divide and conquer
|
||||||
|
|
||||||
val computeBatchFn: Vector[OracleOutcome] => Future[
|
//we want a batch size of at least 1
|
||||||
Vector[(OracleOutcome, ECAdaptorSignature)]] = {
|
val size =
|
||||||
case outcomes: Vector[OracleOutcome] =>
|
Math.max(adaptorPoints.length / Runtime.getRuntime.availableProcessors(),
|
||||||
|
1)
|
||||||
|
|
||||||
|
val computeBatchFn: Vector[Indexed[ECPublicKey]] => Future[
|
||||||
|
Vector[(ECPublicKey, ECAdaptorSignature)]] = {
|
||||||
|
adaptorPoints: Vector[Indexed[ECPublicKey]] =>
|
||||||
Future {
|
Future {
|
||||||
signCETs(outcomes)
|
signCETs(adaptorPoints)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val cetSigsF: Future[Vector[(OracleOutcome, ECAdaptorSignature)]] = {
|
val cetSigsF: Future[Vector[(ECPublicKey, ECAdaptorSignature)]] = {
|
||||||
FutureUtil.batchAndParallelExecute(elements = outcomes,
|
FutureUtil.batchAndParallelExecute(elements = adaptorPoints,
|
||||||
f = computeBatchFn)
|
f = computeBatchFn,
|
||||||
|
batchSize = size)
|
||||||
}.map(_.flatten)
|
}.map(_.flatten)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
@ -216,29 +226,31 @@ case class DLCTxSigner(
|
||||||
|
|
||||||
/** Creates all of this party's CETSignatures */
|
/** Creates all of this party's CETSignatures */
|
||||||
def createCETsAndCETSigs(): (CETSignatures, Vector[WitnessTransaction]) = {
|
def createCETsAndCETSigs(): (CETSignatures, Vector[WitnessTransaction]) = {
|
||||||
val cetsAndSigs = buildAndSignCETs(builder.contractInfo.allOutcomes)
|
val adaptorPoints = builder.contractInfo.adaptorPointsIndexed
|
||||||
|
val cetsAndSigs = buildAndSignCETs(adaptorPoints)
|
||||||
val (msgs, cets, sigs) = cetsAndSigs.unzip3
|
val (msgs, cets, sigs) = cetsAndSigs.unzip3
|
||||||
val refundSig = signRefundTx
|
val refundSig = signRefundTx
|
||||||
|
|
||||||
(CETSignatures(msgs.zip(sigs), refundSig), cets)
|
(CETSignatures(msgs.zip(sigs), refundSig), cets)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The equivalent of [[createCETsAndCETSigs()]] but async */
|
/** The equivalent of [[createCETsAndCETSigs()]] but async */
|
||||||
def createCETsAndCETSigsAsync()(implicit
|
def createCETsAndCETSigsAsync()(implicit
|
||||||
ec: ExecutionContext): Future[(CETSignatures, Vector[WitnessTransaction])] = {
|
ec: ExecutionContext): Future[(CETSignatures, Vector[WitnessTransaction])] = {
|
||||||
val outcomes = builder.contractInfo.allOutcomes
|
val adaptorPoints = builder.contractInfo.adaptorPointsIndexed
|
||||||
val fn = { outcomes: Vector[OracleOutcome] =>
|
val fn = { adaptorPoints: Vector[Indexed[ECPublicKey]] =>
|
||||||
Future {
|
Future {
|
||||||
buildAndSignCETs(outcomes)
|
buildAndSignCETs(adaptorPoints)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val cetsAndSigsF: Future[Vector[
|
val cetsAndSigsF: Future[
|
||||||
Vector[(OracleOutcome, WitnessTransaction, ECAdaptorSignature)]]] = {
|
Vector[Vector[(ECPublicKey, WitnessTransaction, ECAdaptorSignature)]]] = {
|
||||||
FutureUtil.batchAndParallelExecute[OracleOutcome,
|
FutureUtil.batchAndParallelExecute[Indexed[ECPublicKey],
|
||||||
Vector[(
|
Vector[(
|
||||||
OracleOutcome,
|
ECPublicKey,
|
||||||
WitnessTransaction,
|
WitnessTransaction,
|
||||||
ECAdaptorSignature)]](elements =
|
ECAdaptorSignature)]](
|
||||||
outcomes,
|
elements = adaptorPoints,
|
||||||
f = fn)
|
f = fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,7 +264,8 @@ case class DLCTxSigner(
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates this party's CETSignatures given the outcomes and their unsigned CETs */
|
/** Creates this party's CETSignatures given the outcomes and their unsigned CETs */
|
||||||
def createCETSigs(outcomesAndCETs: Vector[OutcomeCETPair]): CETSignatures = {
|
def createCETSigs(
|
||||||
|
outcomesAndCETs: Vector[AdaptorPointCETPair]): CETSignatures = {
|
||||||
val cetSigs = signGivenCETs(outcomesAndCETs)
|
val cetSigs = signGivenCETs(outcomesAndCETs)
|
||||||
val refundSig = signRefundTx
|
val refundSig = signRefundTx
|
||||||
|
|
||||||
|
@ -296,38 +309,37 @@ object DLCTxSigner {
|
||||||
}
|
}
|
||||||
|
|
||||||
def signCET(
|
def signCET(
|
||||||
outcome: OracleOutcome,
|
sigPoint: ECPublicKey,
|
||||||
cet: WitnessTransaction,
|
cet: WitnessTransaction,
|
||||||
cetSigningInfo: ECSignatureParams[P2WSHV0InputInfo],
|
cetSigningInfo: ECSignatureParams[P2WSHV0InputInfo],
|
||||||
fundingKey: AdaptorSign): ECAdaptorSignature = {
|
fundingKey: AdaptorSign): ECAdaptorSignature = {
|
||||||
signCETs(Vector(OutcomeCETPair(outcome, cet)),
|
signCETs(Vector(AdaptorPointCETPair(sigPoint, cet)),
|
||||||
cetSigningInfo,
|
cetSigningInfo,
|
||||||
fundingKey).head._2
|
fundingKey).head._2
|
||||||
}
|
}
|
||||||
|
|
||||||
def signCETs(
|
def signCETs(
|
||||||
outcomesAndCETs: Vector[OutcomeCETPair],
|
outcomesAndCETs: Vector[AdaptorPointCETPair],
|
||||||
cetSigningInfo: ECSignatureParams[P2WSHV0InputInfo],
|
cetSigningInfo: ECSignatureParams[P2WSHV0InputInfo],
|
||||||
fundingKey: AdaptorSign): Vector[(OracleOutcome, ECAdaptorSignature)] = {
|
fundingKey: AdaptorSign): Vector[(ECPublicKey, ECAdaptorSignature)] = {
|
||||||
buildAndSignCETs(outcomesAndCETs, cetSigningInfo, fundingKey).map {
|
buildAndSignCETs(outcomesAndCETs, cetSigningInfo, fundingKey).map {
|
||||||
case (outcome, _, sig) => outcome -> sig
|
case (outcome, _, sig) => outcome -> sig
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def buildAndSignCETs(
|
def buildAndSignCETs(
|
||||||
outcomesAndCETs: Vector[OutcomeCETPair],
|
outcomesAndCETs: Vector[AdaptorPointCETPair],
|
||||||
cetSigningInfo: ECSignatureParams[P2WSHV0InputInfo],
|
cetSigningInfo: ECSignatureParams[P2WSHV0InputInfo],
|
||||||
fundingKey: AdaptorSign): Vector[
|
fundingKey: AdaptorSign): Vector[
|
||||||
(OracleOutcome, WitnessTransaction, ECAdaptorSignature)] = {
|
(ECPublicKey, WitnessTransaction, ECAdaptorSignature)] = {
|
||||||
outcomesAndCETs.map { case OutcomeCETPair(outcome, cet) =>
|
outcomesAndCETs.map { case AdaptorPointCETPair(sigPoint, cet) =>
|
||||||
val adaptorPoint = outcome.sigPoint
|
|
||||||
val hashToSign =
|
val hashToSign =
|
||||||
TransactionSignatureSerializer.hashForSignature(cet,
|
TransactionSignatureSerializer.hashForSignature(cet,
|
||||||
cetSigningInfo,
|
cetSigningInfo,
|
||||||
HashType.sigHashAll)
|
HashType.sigHashAll)
|
||||||
|
|
||||||
val adaptorSig = fundingKey.adaptorSign(adaptorPoint, hashToSign.bytes)
|
val adaptorSig = fundingKey.adaptorSign(sigPoint, hashToSign.bytes)
|
||||||
(outcome, cet, adaptorSig)
|
(sigPoint, cet, adaptorSig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,7 +383,7 @@ object DLCTxSigner {
|
||||||
.map(_.asInstanceOf[WitnessTransaction])
|
.map(_.asInstanceOf[WitnessTransaction])
|
||||||
|
|
||||||
cetT match {
|
cetT match {
|
||||||
case Success(cet) => cet.asInstanceOf[WitnessTransaction]
|
case Success(cet) => cet
|
||||||
case Failure(err) => throw err
|
case Failure(err) => throw err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,14 +10,13 @@ import org.bitcoins.core.policy.Policy
|
||||||
import org.bitcoins.core.protocol.dlc.build.DLCTxBuilder
|
import org.bitcoins.core.protocol.dlc.build.DLCTxBuilder
|
||||||
import org.bitcoins.core.protocol.dlc.models.{
|
import org.bitcoins.core.protocol.dlc.models.{
|
||||||
DLCFundingInput,
|
DLCFundingInput,
|
||||||
FundingSignatures,
|
FundingSignatures
|
||||||
OracleOutcome
|
|
||||||
}
|
}
|
||||||
import org.bitcoins.core.protocol.transaction.{Transaction, WitnessTransaction}
|
import org.bitcoins.core.protocol.transaction.{Transaction, WitnessTransaction}
|
||||||
import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
|
import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
|
||||||
import org.bitcoins.core.psbt.PSBT
|
import org.bitcoins.core.psbt.PSBT
|
||||||
import org.bitcoins.core.script.crypto.HashType
|
import org.bitcoins.core.script.crypto.HashType
|
||||||
import org.bitcoins.core.util.FutureUtil
|
import org.bitcoins.core.util.{FutureUtil, Indexed}
|
||||||
import org.bitcoins.crypto.{ECAdaptorSignature, ECPublicKey}
|
import org.bitcoins.crypto.{ECAdaptorSignature, ECPublicKey}
|
||||||
import scodec.bits.ByteVector
|
import scodec.bits.ByteVector
|
||||||
|
|
||||||
|
@ -37,15 +36,17 @@ case class DLCSignatureVerifier(builder: DLCTxBuilder, isInitiator: Boolean) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Verifies remote's CET signature for a given outcome hash */
|
/** Verifies remote's CET signature for a given outcome hash */
|
||||||
def verifyCETSig(outcome: OracleOutcome, sig: ECAdaptorSignature): Boolean = {
|
def verifyCETSig(
|
||||||
|
adaptorPoint: Indexed[ECPublicKey],
|
||||||
|
sig: ECAdaptorSignature): Boolean = {
|
||||||
val remoteFundingPubKey = if (isInitiator) {
|
val remoteFundingPubKey = if (isInitiator) {
|
||||||
builder.acceptFundingKey
|
builder.acceptFundingKey
|
||||||
} else {
|
} else {
|
||||||
builder.offerFundingKey
|
builder.offerFundingKey
|
||||||
}
|
}
|
||||||
val cet = builder.buildCET(outcome)
|
val cet = builder.buildCET(adaptorPoint)
|
||||||
|
|
||||||
DLCSignatureVerifier.validateCETSignature(outcome,
|
DLCSignatureVerifier.validateCETSignature(adaptorPoint.element,
|
||||||
sig,
|
sig,
|
||||||
remoteFundingPubKey,
|
remoteFundingPubKey,
|
||||||
fundingTx,
|
fundingTx,
|
||||||
|
@ -53,14 +54,14 @@ case class DLCSignatureVerifier(builder: DLCTxBuilder, isInitiator: Boolean) {
|
||||||
cet)
|
cet)
|
||||||
}
|
}
|
||||||
|
|
||||||
def verifyCETSigs(sigs: Vector[(OracleOutcome, ECAdaptorSignature)])(implicit
|
def verifyCETSigs(sigs: Vector[(Indexed[ECPublicKey], ECAdaptorSignature)])(
|
||||||
ec: ExecutionContext): Future[Boolean] = {
|
implicit ec: ExecutionContext): Future[Boolean] = {
|
||||||
val correctNumberOfSigs =
|
val correctNumberOfSigs =
|
||||||
sigs.size >= builder.contractInfo.allOutcomes.length
|
sigs.size >= builder.contractInfo.allOutcomes.length
|
||||||
|
|
||||||
def runVerify(
|
def runVerify(
|
||||||
outcomeSigs: Vector[(OracleOutcome, ECAdaptorSignature)]): Future[
|
outcomeSigs: Vector[
|
||||||
Boolean] = {
|
(Indexed[ECPublicKey], ECAdaptorSignature)]): Future[Boolean] = {
|
||||||
Future {
|
Future {
|
||||||
outcomeSigs.foldLeft(true) { case (ret, (outcome, sig)) =>
|
outcomeSigs.foldLeft(true) { case (ret, (outcome, sig)) =>
|
||||||
ret && verifyCETSig(outcome, sig)
|
ret && verifyCETSig(outcome, sig)
|
||||||
|
@ -89,15 +90,13 @@ case class DLCSignatureVerifier(builder: DLCTxBuilder, isInitiator: Boolean) {
|
||||||
object DLCSignatureVerifier {
|
object DLCSignatureVerifier {
|
||||||
|
|
||||||
def validateCETSignature(
|
def validateCETSignature(
|
||||||
outcome: OracleOutcome,
|
adaptorPoint: ECPublicKey,
|
||||||
sig: ECAdaptorSignature,
|
sig: ECAdaptorSignature,
|
||||||
remoteFundingPubKey: ECPublicKey,
|
remoteFundingPubKey: ECPublicKey,
|
||||||
fundingTx: Transaction,
|
fundingTx: Transaction,
|
||||||
fundOutputIndex: Int,
|
fundOutputIndex: Int,
|
||||||
cet: WitnessTransaction
|
cet: WitnessTransaction
|
||||||
): Boolean = {
|
): Boolean = {
|
||||||
val adaptorPoint = outcome.sigPoint
|
|
||||||
|
|
||||||
val sigComponent = WitnessTxSigComponentRaw(
|
val sigComponent = WitnessTxSigComponentRaw(
|
||||||
transaction = cet,
|
transaction = cet,
|
||||||
inputIndex = UInt32.zero,
|
inputIndex = UInt32.zero,
|
||||||
|
|
|
@ -103,12 +103,18 @@ object FutureUtil {
|
||||||
elements: Vector[T],
|
elements: Vector[T],
|
||||||
f: Vector[T] => Future[U],
|
f: Vector[T] => Future[U],
|
||||||
batchSize: Int)(implicit ec: ExecutionContext): Future[Vector[U]] = {
|
batchSize: Int)(implicit ec: ExecutionContext): Future[Vector[U]] = {
|
||||||
require(batchSize > 0, s"Cannot have batch size 0 or less, got=$batchSize")
|
require(
|
||||||
|
batchSize > 0,
|
||||||
|
s"Cannot have batch size less than or equal to zero, got=$batchSize")
|
||||||
|
if (elements.isEmpty) {
|
||||||
|
Future.successful(Vector.empty)
|
||||||
|
} else {
|
||||||
val batches = elements.grouped(batchSize).toVector
|
val batches = elements.grouped(batchSize).toVector
|
||||||
val execute: Vector[Future[U]] = batches.map(b => f(b))
|
val execute: Vector[Future[U]] = batches.map(b => f(b))
|
||||||
val doneF = Future.sequence(execute)
|
val doneF = Future.sequence(execute)
|
||||||
doneF
|
doneF
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Same as [[batchAndParallelExecute()]], but computes the batchSize based on the
|
/** Same as [[batchAndParallelExecute()]], but computes the batchSize based on the
|
||||||
* number of available processors on your machine
|
* number of available processors on your machine
|
||||||
|
|
|
@ -102,6 +102,10 @@ sealed trait PublicKey extends NetworkElement {
|
||||||
fromBytes(x.+:(leadByte))
|
fromBytes(x.+:(leadByte))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def hashCode: Int = {
|
||||||
|
bytes.hashCode
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Wraps raw ECPublicKey bytes without doing any validation or deserialization (may be invalid). */
|
/** Wraps raw ECPublicKey bytes without doing any validation or deserialization (may be invalid). */
|
||||||
|
|
|
@ -153,14 +153,16 @@ class DbCommonsColumnMappers(val profile: JdbcProfile) {
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit val uint64Mapper: BaseColumnType[UInt64] = {
|
implicit val uint64Mapper: BaseColumnType[UInt64] = {
|
||||||
MappedColumnType.base[UInt64, BigDecimal](
|
MappedColumnType.base[UInt64, String](
|
||||||
{ u64: UInt64 =>
|
{ u64: UInt64 =>
|
||||||
BigDecimal(u64.toBigInt.bigInteger)
|
val bytes = u64.bytes
|
||||||
|
val padded = if (bytes.length <= 8) {
|
||||||
|
bytes.padLeft(8)
|
||||||
|
} else bytes
|
||||||
|
|
||||||
|
padded.toHex
|
||||||
},
|
},
|
||||||
//this has the potential to throw
|
UInt64.fromHex
|
||||||
{ bigDec: BigDecimal =>
|
|
||||||
UInt64(bigDec.toBigIntExact.get)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ import org.bitcoins.core.protocol.tlv.{
|
||||||
}
|
}
|
||||||
import org.bitcoins.core.protocol.transaction._
|
import org.bitcoins.core.protocol.transaction._
|
||||||
import org.bitcoins.core.script.crypto.HashType
|
import org.bitcoins.core.script.crypto.HashType
|
||||||
import org.bitcoins.core.util.{BitcoinScriptUtil, NumberUtil}
|
import org.bitcoins.core.util.{BitcoinScriptUtil, Indexed, NumberUtil}
|
||||||
import org.bitcoins.core.wallet.utxo._
|
import org.bitcoins.core.wallet.utxo._
|
||||||
import org.bitcoins.crypto._
|
import org.bitcoins.crypto._
|
||||||
import org.bitcoins.testkitcore.dlc.{DLCFeeTestUtil, DLCTest, TestDLCClient}
|
import org.bitcoins.testkitcore.dlc.{DLCFeeTestUtil, DLCTest, TestDLCClient}
|
||||||
|
@ -334,6 +334,39 @@ class DLCClientTest extends BitcoinSJvmTest with DLCTest {
|
||||||
assert(!acceptVerifier.verifyRemoteFundingSigs(acceptFundingSigs))
|
assert(!acceptVerifier.verifyRemoteFundingSigs(acceptFundingSigs))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it should "succeed on valid CET signatures" in {
|
||||||
|
val (offerClient, acceptClient, outcomes) =
|
||||||
|
constructDLCClients(numOutcomesOrDigits = 2,
|
||||||
|
isNumeric = false,
|
||||||
|
oracleThreshold = 1,
|
||||||
|
numOracles = 1,
|
||||||
|
paramsOpt = None)
|
||||||
|
val builder = offerClient.dlcTxBuilder
|
||||||
|
val offerVerifier = DLCSignatureVerifier(builder, isInitiator = true)
|
||||||
|
val acceptVerifier = DLCSignatureVerifier(builder, isInitiator = false)
|
||||||
|
|
||||||
|
val offerCETSigs = offerClient.dlcTxSigner.createCETSigs()
|
||||||
|
val acceptCETSigs = acceptClient.dlcTxSigner.createCETSigs()
|
||||||
|
|
||||||
|
outcomes.zipWithIndex.foreach { case (outcomeUncast, index) =>
|
||||||
|
val outcome = EnumOracleOutcome(
|
||||||
|
Vector(offerClient.offer.oracleInfo.asInstanceOf[EnumSingleOracleInfo]),
|
||||||
|
outcomeUncast.asInstanceOf[EnumOutcome])
|
||||||
|
|
||||||
|
assert(
|
||||||
|
offerVerifier.verifyCETSig(Indexed(outcome.sigPoint, index),
|
||||||
|
acceptCETSigs(outcome.sigPoint)))
|
||||||
|
assert(
|
||||||
|
acceptVerifier.verifyCETSig(Indexed(outcome.sigPoint, index),
|
||||||
|
offerCETSigs(outcome.sigPoint)))
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(offerVerifier.verifyRefundSig(acceptCETSigs.refundSig))
|
||||||
|
assert(offerVerifier.verifyRefundSig(offerCETSigs.refundSig))
|
||||||
|
assert(acceptVerifier.verifyRefundSig(offerCETSigs.refundSig))
|
||||||
|
assert(acceptVerifier.verifyRefundSig(acceptCETSigs.refundSig))
|
||||||
|
}
|
||||||
|
|
||||||
it should "fail on invalid CET signatures" in {
|
it should "fail on invalid CET signatures" in {
|
||||||
val (offerClient, acceptClient, outcomes) =
|
val (offerClient, acceptClient, outcomes) =
|
||||||
constructDLCClients(numOutcomesOrDigits = 3,
|
constructDLCClients(numOutcomesOrDigits = 3,
|
||||||
|
@ -360,15 +393,16 @@ class DLCClientTest extends BitcoinSJvmTest with DLCTest {
|
||||||
val oracleSig = genEnumOracleSignature(oracleInfo, outcome.outcome)
|
val oracleSig = genEnumOracleSignature(oracleInfo, outcome.outcome)
|
||||||
|
|
||||||
assertThrows[RuntimeException] {
|
assertThrows[RuntimeException] {
|
||||||
offerClient.dlcTxSigner.completeCET(oracleOutcome,
|
offerClient.dlcTxSigner.completeCET(
|
||||||
badAcceptCETSigs(oracleOutcome),
|
oracleOutcome,
|
||||||
|
badAcceptCETSigs(oracleOutcome.sigPoint),
|
||||||
Vector(oracleSig))
|
Vector(oracleSig))
|
||||||
}
|
}
|
||||||
|
|
||||||
assertThrows[RuntimeException] {
|
assertThrows[RuntimeException] {
|
||||||
acceptClient.dlcTxSigner
|
acceptClient.dlcTxSigner
|
||||||
.completeCET(oracleOutcome,
|
.completeCET(oracleOutcome,
|
||||||
badOfferCETSigs(oracleOutcome),
|
badOfferCETSigs(oracleOutcome.sigPoint),
|
||||||
Vector(oracleSig))
|
Vector(oracleSig))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -381,29 +415,25 @@ class DLCClientTest extends BitcoinSJvmTest with DLCTest {
|
||||||
acceptClient.dlcTxSigner.completeRefundTx(badOfferCETSigs.refundSig)
|
acceptClient.dlcTxSigner.completeRefundTx(badOfferCETSigs.refundSig)
|
||||||
}
|
}
|
||||||
|
|
||||||
outcomes.foreach { outcomeUncast =>
|
outcomes.zipWithIndex.foreach { case (outcomeUncast, index) =>
|
||||||
val outcome = EnumOracleOutcome(
|
val outcome = EnumOracleOutcome(
|
||||||
Vector(offerClient.offer.oracleInfo.asInstanceOf[EnumSingleOracleInfo]),
|
Vector(offerClient.offer.oracleInfo.asInstanceOf[EnumSingleOracleInfo]),
|
||||||
outcomeUncast.asInstanceOf[EnumOutcome])
|
outcomeUncast.asInstanceOf[EnumOutcome])
|
||||||
|
val adaptorPoint = Indexed(outcome.sigPoint, index)
|
||||||
|
|
||||||
assert(offerVerifier.verifyCETSig(outcome, acceptCETSigs(outcome)))
|
assert(
|
||||||
assert(acceptVerifier.verifyCETSig(outcome, offerCETSigs(outcome)))
|
!offerVerifier.verifyCETSig(adaptorPoint,
|
||||||
}
|
badAcceptCETSigs(outcome.sigPoint)))
|
||||||
assert(offerVerifier.verifyRefundSig(acceptCETSigs.refundSig))
|
assert(
|
||||||
assert(offerVerifier.verifyRefundSig(offerCETSigs.refundSig))
|
!acceptVerifier.verifyCETSig(adaptorPoint,
|
||||||
assert(acceptVerifier.verifyRefundSig(offerCETSigs.refundSig))
|
badOfferCETSigs(outcome.sigPoint)))
|
||||||
assert(acceptVerifier.verifyRefundSig(acceptCETSigs.refundSig))
|
|
||||||
|
|
||||||
outcomes.foreach { outcomeUncast =>
|
assert(
|
||||||
val outcome = EnumOracleOutcome(
|
!offerVerifier.verifyCETSig(adaptorPoint,
|
||||||
Vector(offerClient.offer.oracleInfo.asInstanceOf[EnumSingleOracleInfo]),
|
offerCETSigs(outcome.sigPoint)))
|
||||||
outcomeUncast.asInstanceOf[EnumOutcome])
|
assert(
|
||||||
|
!acceptVerifier.verifyCETSig(adaptorPoint,
|
||||||
assert(!offerVerifier.verifyCETSig(outcome, badAcceptCETSigs(outcome)))
|
acceptCETSigs(outcome.sigPoint)))
|
||||||
assert(!acceptVerifier.verifyCETSig(outcome, badOfferCETSigs(outcome)))
|
|
||||||
|
|
||||||
assert(!offerVerifier.verifyCETSig(outcome, offerCETSigs(outcome)))
|
|
||||||
assert(!acceptVerifier.verifyCETSig(outcome, acceptCETSigs(outcome)))
|
|
||||||
}
|
}
|
||||||
assert(!offerVerifier.verifyRefundSig(badAcceptCETSigs.refundSig))
|
assert(!offerVerifier.verifyRefundSig(badAcceptCETSigs.refundSig))
|
||||||
assert(!offerVerifier.verifyRefundSig(badOfferCETSigs.refundSig))
|
assert(!offerVerifier.verifyRefundSig(badOfferCETSigs.refundSig))
|
||||||
|
@ -411,6 +441,36 @@ class DLCClientTest extends BitcoinSJvmTest with DLCTest {
|
||||||
assert(!acceptVerifier.verifyRefundSig(badAcceptCETSigs.refundSig))
|
assert(!acceptVerifier.verifyRefundSig(badAcceptCETSigs.refundSig))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it should "compute sigpoints correctly" in {
|
||||||
|
runTestsForParam(Vector(4, 6, 8)) { numDigitsOrOutcomes =>
|
||||||
|
runTestsForParam(Vector(true, false)) { isNumeric =>
|
||||||
|
runTestsForParam(Vector((1, 1), (2, 3), (3, 5))) {
|
||||||
|
case (threshold, numOracles) =>
|
||||||
|
runTestsForParam(
|
||||||
|
Vector(None,
|
||||||
|
Some(
|
||||||
|
OracleParamsV0TLV(numDigitsOrOutcomes / 2 + 1,
|
||||||
|
numDigitsOrOutcomes / 2,
|
||||||
|
maximizeCoverage = true)))) {
|
||||||
|
oracleParams =>
|
||||||
|
val (client, _, _) = constructDLCClients(numDigitsOrOutcomes,
|
||||||
|
isNumeric,
|
||||||
|
threshold,
|
||||||
|
numOracles,
|
||||||
|
oracleParams)
|
||||||
|
val contract = client.offer.contractInfo
|
||||||
|
val outcomes = contract.allOutcomes
|
||||||
|
|
||||||
|
val adaptorPoints = contract.adaptorPoints
|
||||||
|
val expectedAdaptorPoints = outcomes.map(_.sigPoint)
|
||||||
|
|
||||||
|
assert(adaptorPoints == expectedAdaptorPoints)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def assertCorrectSigDerivation(
|
def assertCorrectSigDerivation(
|
||||||
offerSetup: SetupDLC,
|
offerSetup: SetupDLC,
|
||||||
dlcOffer: TestDLCClient,
|
dlcOffer: TestDLCClient,
|
||||||
|
|
|
@ -54,9 +54,10 @@ class SetupDLCTest extends BitcoinSJvmTest {
|
||||||
refundTx: WitnessTransaction = validRefundTx): SetupDLC = {
|
refundTx: WitnessTransaction = validRefundTx): SetupDLC = {
|
||||||
SetupDLC(
|
SetupDLC(
|
||||||
fundingTx = fundingTx,
|
fundingTx = fundingTx,
|
||||||
cets = Vector(
|
cets = Vector(EnumOracleOutcome(Vector(oracleInfo),
|
||||||
EnumOracleOutcome(Vector(oracleInfo), EnumOutcome("WIN")) -> cet0,
|
EnumOutcome("WIN")).sigPoint -> cet0,
|
||||||
EnumOracleOutcome(Vector(oracleInfo), EnumOutcome("LOSE")) -> cet1),
|
EnumOracleOutcome(Vector(oracleInfo),
|
||||||
|
EnumOutcome("LOSE")).sigPoint -> cet1),
|
||||||
refundTx = refundTx
|
refundTx = refundTx
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -207,7 +207,7 @@ object DLCTLVGen {
|
||||||
ECPublicKey.freshPublicKey): CETSignatures = {
|
ECPublicKey.freshPublicKey): CETSignatures = {
|
||||||
CETSignatures(
|
CETSignatures(
|
||||||
outcomes.map(outcome =>
|
outcomes.map(outcome =>
|
||||||
EnumOracleOutcome(Vector(oracleInfo), outcome) -> adaptorSig),
|
EnumOracleOutcome(Vector(oracleInfo), outcome).sigPoint -> adaptorSig),
|
||||||
partialSig(fundingPubKey, sigHashByte = false))
|
partialSig(fundingPubKey, sigHashByte = false))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.bitcoins.core.protocol.transaction.{
|
||||||
}
|
}
|
||||||
import org.bitcoins.core.protocol.{BitcoinAddress, BlockTimeStamp}
|
import org.bitcoins.core.protocol.{BitcoinAddress, BlockTimeStamp}
|
||||||
import org.bitcoins.core.script.crypto.HashType
|
import org.bitcoins.core.script.crypto.HashType
|
||||||
|
import org.bitcoins.core.util.Indexed
|
||||||
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
|
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
|
||||||
import org.bitcoins.core.wallet.utxo.{
|
import org.bitcoins.core.wallet.utxo.{
|
||||||
ConditionalPath,
|
ConditionalPath,
|
||||||
|
@ -186,12 +187,13 @@ case class ValidTestInputs(
|
||||||
def buildTransactions: DLCTransactions = {
|
def buildTransactions: DLCTransactions = {
|
||||||
val builder = this.builder
|
val builder = this.builder
|
||||||
val fundingTx = builder.buildFundingTx
|
val fundingTx = builder.buildFundingTx
|
||||||
val cets =
|
val adaptorPoints =
|
||||||
params.contractInfo
|
params.contractInfo
|
||||||
.map(_.preImage)
|
.map(_.preImage)
|
||||||
.map(EnumOutcome.apply)
|
.map(EnumOutcome.apply)
|
||||||
.map(outcome => EnumOracleOutcome(Vector(params.oracleInfo), outcome))
|
.map(outcome => EnumOracleOutcome(Vector(params.oracleInfo), outcome))
|
||||||
.map(builder.buildCET)
|
.map(_.sigPoint)
|
||||||
|
val cets = builder.buildCETs(Indexed(adaptorPoints))
|
||||||
val refundTx = builder.buildRefundTx
|
val refundTx = builder.buildRefundTx
|
||||||
|
|
||||||
DLCTransactions(fundingTx, cets, refundTx)
|
DLCTransactions(fundingTx, cets, refundTx)
|
||||||
|
|
|
@ -246,7 +246,7 @@ object DLCTxGen {
|
||||||
EnumOracleOutcome(Vector(inputs.params.oracleInfo),
|
EnumOracleOutcome(Vector(inputs.params.oracleInfo),
|
||||||
EnumOutcome(outcomeStr))
|
EnumOutcome(outcomeStr))
|
||||||
|
|
||||||
val accpetCETSigs = acceptSigner.createCETSigs()
|
val acceptCETSigs = acceptSigner.createCETSigs()
|
||||||
val offerCETSigs = offerSigner.createCETSigs()
|
val offerCETSigs = offerSigner.createCETSigs()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
@ -256,23 +256,23 @@ object DLCTxGen {
|
||||||
|
|
||||||
signedFundingTx <- acceptSigner.completeFundingTx(offerFundingSigs)
|
signedFundingTx <- acceptSigner.completeFundingTx(offerFundingSigs)
|
||||||
} yield {
|
} yield {
|
||||||
val signedRefundTx = offerSigner.completeRefundTx(accpetCETSigs.refundSig)
|
val signedRefundTx = offerSigner.completeRefundTx(acceptCETSigs.refundSig)
|
||||||
|
|
||||||
val offerSignedCET = offerSigner.completeCET(
|
val offerSignedCET = offerSigner.completeCET(
|
||||||
outcome,
|
outcome,
|
||||||
accpetCETSigs(outcome),
|
acceptCETSigs(outcome.sigPoint),
|
||||||
Vector(
|
Vector(
|
||||||
EnumOracleSignature(inputs.params.oracleInfo,
|
EnumOracleSignature(inputs.params.oracleInfo,
|
||||||
inputs.params.oracleSignature)))
|
inputs.params.oracleSignature)))
|
||||||
|
|
||||||
val acceptSignedCET = acceptSigner.completeCET(
|
val acceptSignedCET = acceptSigner.completeCET(
|
||||||
outcome,
|
outcome,
|
||||||
offerCETSigs(outcome),
|
offerCETSigs(outcome.sigPoint),
|
||||||
Vector(
|
Vector(
|
||||||
EnumOracleSignature(inputs.params.oracleInfo,
|
EnumOracleSignature(inputs.params.oracleInfo,
|
||||||
inputs.params.oracleSignature)))
|
inputs.params.oracleSignature)))
|
||||||
|
|
||||||
val accept = acceptWithoutSigs.withSigs(accpetCETSigs)
|
val accept = acceptWithoutSigs.withSigs(acceptCETSigs)
|
||||||
|
|
||||||
val contractId = fundingTx.txIdBE.bytes.xor(accept.tempContractId.bytes)
|
val contractId = fundingTx.txIdBE.bytes.xor(accept.tempContractId.bytes)
|
||||||
val sign = DLCSign(offerCETSigs, offerFundingSigs, contractId)
|
val sign = DLCSign(offerCETSigs, offerFundingSigs, contractId)
|
||||||
|
|
|
@ -98,7 +98,7 @@ case class TestDLCClient(
|
||||||
}
|
}
|
||||||
cetSigs =
|
cetSigs =
|
||||||
dlcTxSigner.createCETSigs(setupDLCWithoutFundingTxSigs.cets.map {
|
dlcTxSigner.createCETSigs(setupDLCWithoutFundingTxSigs.cets.map {
|
||||||
case (msg, info) => OutcomeCETPair(msg, info.tx)
|
case (msg, info) => AdaptorPointCETPair(msg, info.tx)
|
||||||
})
|
})
|
||||||
localFundingSigs <- Future.fromTry {
|
localFundingSigs <- Future.fromTry {
|
||||||
dlcTxSigner.signFundingTx()
|
dlcTxSigner.signFundingTx()
|
||||||
|
|
|
@ -3,10 +3,10 @@ package org.bitcoins.testkit
|
||||||
import com.typesafe.config._
|
import com.typesafe.config._
|
||||||
import org.bitcoins.dlc.oracle.config.DLCOracleAppConfig
|
import org.bitcoins.dlc.oracle.config.DLCOracleAppConfig
|
||||||
import org.bitcoins.server.BitcoinSAppConfig
|
import org.bitcoins.server.BitcoinSAppConfig
|
||||||
import org.bitcoins.testkitcore.Implicits.GeneratorOps
|
|
||||||
import org.bitcoins.testkitcore.gen.{NumberGenerator, StringGenerators}
|
|
||||||
import org.bitcoins.testkit.keymanager.KeyManagerTestUtil
|
import org.bitcoins.testkit.keymanager.KeyManagerTestUtil
|
||||||
import org.bitcoins.testkit.util.FileUtil
|
import org.bitcoins.testkit.util.FileUtil
|
||||||
|
import org.bitcoins.testkitcore.Implicits.GeneratorOps
|
||||||
|
import org.bitcoins.testkitcore.gen.{NumberGenerator, StringGenerators}
|
||||||
|
|
||||||
import java.nio.file._
|
import java.nio.file._
|
||||||
import scala.concurrent.ExecutionContext
|
import scala.concurrent.ExecutionContext
|
||||||
|
@ -149,9 +149,10 @@ object BitcoinSTestAppConfig {
|
||||||
case object Node extends ProjectType
|
case object Node extends ProjectType
|
||||||
case object Chain extends ProjectType
|
case object Chain extends ProjectType
|
||||||
case object Oracle extends ProjectType
|
case object Oracle extends ProjectType
|
||||||
|
case object DLC extends ProjectType
|
||||||
case object Test extends ProjectType
|
case object Test extends ProjectType
|
||||||
|
|
||||||
val all = List(Wallet, Node, Chain, Oracle, Test)
|
val all = List(Wallet, Node, Chain, Oracle, DLC, Test)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Generates a Typesafe config with DBs set to memory
|
/** Generates a Typesafe config with DBs set to memory
|
||||||
|
@ -178,6 +179,7 @@ object BitcoinSTestAppConfig {
|
||||||
case ProjectType.Chain => "chain"
|
case ProjectType.Chain => "chain"
|
||||||
case ProjectType.Node => "node"
|
case ProjectType.Node => "node"
|
||||||
case ProjectType.Oracle => "oracle"
|
case ProjectType.Oracle => "oracle"
|
||||||
|
case ProjectType.DLC => "dlc"
|
||||||
case ProjectType.Test => "test"
|
case ProjectType.Test => "test"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue