mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-03 10:46:42 +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
|
||||
}
|
||||
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.ln.currency.MilliSatoshis
|
||||
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.wallet.fee.SatoshisPerVirtualByte
|
||||
import org.bitcoins.core.wallet.utxo.AddressLabelTag
|
||||
import org.bitcoins.crypto.{
|
||||
AesPassword,
|
||||
DoubleSha256DigestBE,
|
||||
ECPublicKey,
|
||||
Sha256DigestBE
|
||||
}
|
||||
import org.bitcoins.crypto._
|
||||
import scodec.bits.ByteVector
|
||||
import scopt.OParser
|
||||
import ujson._
|
||||
|
@ -238,7 +233,8 @@ object ConsoleCli {
|
|||
),
|
||||
cmd("acceptdlcofferfromfile")
|
||||
.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")
|
||||
.children(
|
||||
arg[Path]("path")
|
||||
|
@ -248,6 +244,14 @@ object ConsoleCli {
|
|||
case accept: AcceptDLCOfferFromFile =>
|
||||
accept.copy(path = path)
|
||||
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")
|
||||
|
@ -265,7 +269,7 @@ object ConsoleCli {
|
|||
),
|
||||
cmd("signdlcfromfile")
|
||||
.action((_, conf) =>
|
||||
conf.copy(command = SignDLCFromFile(new File("").toPath)))
|
||||
conf.copy(command = SignDLCFromFile(new File("").toPath, None)))
|
||||
.text("Signs a DLC")
|
||||
.children(
|
||||
arg[Path]("path")
|
||||
|
@ -275,6 +279,14 @@ object ConsoleCli {
|
|||
case signDLC: SignDLCFromFile =>
|
||||
signDLC.copy(path = path)
|
||||
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")
|
||||
|
@ -383,6 +395,20 @@ object ConsoleCli {
|
|||
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")
|
||||
.action((_, conf) => conf.copy(command = GetDLCs))
|
||||
.text("Returns all dlcs in the wallet"),
|
||||
|
@ -1440,12 +1466,13 @@ object ConsoleCli {
|
|||
)
|
||||
case AcceptDLCOffer(offer) =>
|
||||
RequestParam("acceptdlcoffer", Seq(up.writeJs(offer)))
|
||||
case AcceptDLCOfferFromFile(path) =>
|
||||
RequestParam("acceptdlcofferfromfile", Seq(up.writeJs(path)))
|
||||
case AcceptDLCOfferFromFile(path, dest) =>
|
||||
RequestParam("acceptdlcofferfromfile",
|
||||
Seq(up.writeJs(path), up.writeJs(dest)))
|
||||
case SignDLC(accept) =>
|
||||
RequestParam("signdlc", Seq(up.writeJs(accept)))
|
||||
case SignDLCFromFile(path) =>
|
||||
RequestParam("signdlcfromfile", Seq(up.writeJs(path)))
|
||||
case SignDLCFromFile(path, dest) =>
|
||||
RequestParam("signdlcfromfile", Seq(up.writeJs(path), up.writeJs(dest)))
|
||||
case AddDLCSigs(sigs) =>
|
||||
RequestParam("adddlcsigs", Seq(up.writeJs(sigs)))
|
||||
case AddDLCSigsFromFile(path) =>
|
||||
|
@ -1462,6 +1489,8 @@ object ConsoleCli {
|
|||
case ExecuteDLCRefund(contractId, noBroadcast) =>
|
||||
RequestParam("executedlcrefund",
|
||||
Seq(up.writeJs(contractId), up.writeJs(noBroadcast)))
|
||||
case CancelDLC(paramHash) =>
|
||||
RequestParam("canceldlc", Seq(up.writeJs(paramHash)))
|
||||
// Wallet
|
||||
case GetBalance(isSats) =>
|
||||
RequestParam("getbalance", Seq(up.writeJs(isSats)))
|
||||
|
@ -1800,13 +1829,15 @@ object CliCommand {
|
|||
case class AcceptDLCOffer(offer: LnMessage[DLCOfferTLV])
|
||||
extends AcceptDLCCliCommand
|
||||
|
||||
case class AcceptDLCOfferFromFile(path: Path) extends AcceptDLCCliCommand
|
||||
case class AcceptDLCOfferFromFile(path: Path, destination: Option[Path])
|
||||
extends AcceptDLCCliCommand
|
||||
|
||||
sealed trait SignDLCCliCommand extends AppServerCliCommand
|
||||
|
||||
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
|
||||
|
||||
|
@ -1831,6 +1862,8 @@ object CliCommand {
|
|||
extends AppServerCliCommand
|
||||
with Broadcastable
|
||||
|
||||
case class CancelDLC(paramHash: Sha256DigestBE) extends AppServerCliCommand
|
||||
|
||||
case object GetDLCs extends AppServerCliCommand
|
||||
case class GetDLC(paramHash: Sha256DigestBE) extends AppServerCliCommand
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ class AcceptDLCDialog
|
|||
// TODO figure how to validate when using a file
|
||||
offerDLCFile = None // reset
|
||||
offerFileChosenLabel.text = "" // reset
|
||||
AcceptDLCOfferFromFile(file.toPath)
|
||||
AcceptDLCOfferFromFile(file.toPath, None)
|
||||
case None =>
|
||||
val offerHex = readStringFromNode(inputs(dlcOfferStr))
|
||||
val offer = LnMessageFactory(DLCOfferTLV).fromHex(offerHex)
|
||||
|
|
|
@ -29,7 +29,7 @@ class SignDLCDialog
|
|||
case Some(file) =>
|
||||
acceptDLCFile = None // reset
|
||||
acceptFileChosenLabel.text = "" // reset
|
||||
SignDLCFromFile(file.toPath)
|
||||
SignDLCFromFile(file.toPath, None)
|
||||
case None =>
|
||||
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.protocol.BitcoinAddress
|
||||
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.tlv.EnumOutcome
|
||||
import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
|
||||
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
|
||||
import org.bitcoins.crypto._
|
||||
import org.bitcoins.testkitcore.gen.{LnMessageGen, TLVGen}
|
||||
import org.bitcoins.testkitcore.util.BitcoinSJvmTest
|
||||
|
||||
class DLCMessageTest extends BitcoinSJvmTest {
|
||||
|
@ -80,14 +85,41 @@ class DLCMessageTest extends BitcoinSJvmTest {
|
|||
dummyAddress,
|
||||
payoutSerialId = UInt64.zero,
|
||||
changeSerialId = UInt64.one,
|
||||
CETSignatures(Vector(
|
||||
CETSignatures(
|
||||
Vector(
|
||||
EnumOracleOutcome(
|
||||
Vector(dummyOracle),
|
||||
EnumOutcome(dummyStr)) -> ECAdaptorSignature.dummy),
|
||||
EnumOutcome(dummyStr)).sigPoint -> ECAdaptorSignature.dummy),
|
||||
dummySig),
|
||||
DLCAccept.NoNegotiationFields,
|
||||
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.transaction._
|
||||
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.fee.SatoshisPerVirtualByte
|
||||
import org.bitcoins.core.wallet.utxo.{
|
||||
|
@ -172,14 +173,19 @@ case class DLCTxBuilder(offer: DLCOffer, accept: DLCAcceptWithoutSigs) {
|
|||
/** Constructs the unsigned Contract Execution Transaction (CET)
|
||||
* for a given outcome hash
|
||||
*/
|
||||
def buildCET(msg: OracleOutcome): WitnessTransaction = {
|
||||
buildCETs(Vector(msg)).head
|
||||
def buildCET(adaptorPoint: Indexed[ECPublicKey]): WitnessTransaction = {
|
||||
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
|
||||
.buildCETs(
|
||||
msgs,
|
||||
adaptorPoints,
|
||||
contractInfo,
|
||||
offerFundingKey,
|
||||
offerFinalAddress.scriptPubKey,
|
||||
|
@ -193,8 +199,9 @@ case class DLCTxBuilder(offer: DLCOffer, accept: DLCAcceptWithoutSigs) {
|
|||
)
|
||||
}
|
||||
|
||||
def buildCETs(msgs: Vector[OracleOutcome]): Vector[WitnessTransaction] = {
|
||||
buildCETsMap(msgs).map(_.wtx)
|
||||
def buildCETs(adaptorPoints: Vector[Indexed[ECPublicKey]]): Vector[
|
||||
WitnessTransaction] = {
|
||||
buildCETsMap(adaptorPoints).map(_.wtx)
|
||||
}
|
||||
|
||||
/** Constructs the unsigned refund transaction */
|
||||
|
@ -318,7 +325,7 @@ object DLCTxBuilder {
|
|||
}
|
||||
|
||||
def buildCET(
|
||||
outcome: OracleOutcome,
|
||||
adaptorPoint: Indexed[ECPublicKey],
|
||||
contractInfo: ContractInfo,
|
||||
offerFundingKey: ECPublicKey,
|
||||
offerFinalSPK: ScriptPubKey,
|
||||
|
@ -328,7 +335,8 @@ object DLCTxBuilder {
|
|||
acceptSerialId: UInt64,
|
||||
timeouts: DLCTimeouts,
|
||||
fundingOutputRef: OutputReference): WitnessTransaction = {
|
||||
val Vector(OutcomeCETPair(_, cet)) = buildCETs(Vector(outcome),
|
||||
val Vector(AdaptorPointCETPair(_, cet)) = buildCETs(
|
||||
Vector(adaptorPoint),
|
||||
contractInfo,
|
||||
offerFundingKey,
|
||||
offerFinalSPK,
|
||||
|
@ -337,13 +345,14 @@ object DLCTxBuilder {
|
|||
acceptFinalSPK,
|
||||
acceptSerialId,
|
||||
timeouts,
|
||||
fundingOutputRef)
|
||||
fundingOutputRef
|
||||
)
|
||||
|
||||
cet
|
||||
}
|
||||
|
||||
def buildCETs(
|
||||
outcomes: Vector[OracleOutcome],
|
||||
adaptorPoints: Vector[Indexed[ECPublicKey]],
|
||||
contractInfo: ContractInfo,
|
||||
offerFundingKey: ECPublicKey,
|
||||
offerFinalSPK: ScriptPubKey,
|
||||
|
@ -352,7 +361,7 @@ object DLCTxBuilder {
|
|||
acceptFinalSPK: ScriptPubKey,
|
||||
acceptSerialId: UInt64,
|
||||
timeouts: DLCTimeouts,
|
||||
fundingOutputRef: OutputReference): Vector[OutcomeCETPair] = {
|
||||
fundingOutputRef: OutputReference): Vector[AdaptorPointCETPair] = {
|
||||
val builder =
|
||||
DLCCETBuilder(contractInfo,
|
||||
offerFundingKey,
|
||||
|
@ -364,13 +373,17 @@ object DLCTxBuilder {
|
|||
timeouts,
|
||||
fundingOutputRef)
|
||||
|
||||
outcomes.map { outcome =>
|
||||
OutcomeCETPair(outcome, builder.buildCET(outcome))
|
||||
val outcomes = adaptorPoints.map { case Indexed(_, index) =>
|
||||
contractInfo.allOutcomes(index)
|
||||
}
|
||||
|
||||
adaptorPoints.zip(outcomes).map { case (Indexed(sigPoint, _), outcome) =>
|
||||
AdaptorPointCETPair(sigPoint, builder.buildCET(outcome))
|
||||
}
|
||||
}
|
||||
|
||||
def buildCETs(
|
||||
outcomes: Vector[OracleOutcome],
|
||||
adaptorPoints: Vector[Indexed[ECPublicKey]],
|
||||
contractInfo: ContractInfo,
|
||||
offerFundingKey: ECPublicKey,
|
||||
offerFinalSPK: ScriptPubKey,
|
||||
|
@ -380,13 +393,13 @@ object DLCTxBuilder {
|
|||
acceptSerialId: UInt64,
|
||||
timeouts: DLCTimeouts,
|
||||
fundingTx: Transaction,
|
||||
fundOutputIndex: Int): Vector[OutcomeCETPair] = {
|
||||
fundOutputIndex: Int): Vector[AdaptorPointCETPair] = {
|
||||
val fundingOutPoint =
|
||||
TransactionOutPoint(fundingTx.txId, UInt32(fundOutputIndex))
|
||||
val fundingOutputRef =
|
||||
OutputReference(fundingOutPoint, fundingTx.outputs(fundOutputIndex))
|
||||
|
||||
buildCETs(outcomes,
|
||||
buildCETs(adaptorPoints,
|
||||
contractInfo,
|
||||
offerFundingKey,
|
||||
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
|
||||
* 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 cetSig The actual signature for local's key found on-chain on a CET
|
||||
*/
|
||||
private def sigFromOutcomeAndSigs(
|
||||
outcome: OracleOutcome,
|
||||
adaptorPoint: ECPublicKey,
|
||||
adaptorSig: ECAdaptorSignature,
|
||||
cetSig: ECDigitalSignature): Try[SchnorrDigitalSignature] = {
|
||||
val sigPubKey = outcome.sigPoint
|
||||
|
||||
cetSig: ECDigitalSignature): Try[FieldElement] = {
|
||||
// This value is either the oracle signature S value or it is
|
||||
// useless garbage, but we don't know in this scope, the caller
|
||||
// must do further work to check this.
|
||||
val possibleOracleST = Try {
|
||||
sigPubKey
|
||||
Try {
|
||||
adaptorPoint
|
||||
.extractAdaptorSecret(adaptorSig, ECDigitalSignature(cetSig.bytes.init))
|
||||
.fieldElement
|
||||
}
|
||||
|
||||
possibleOracleST.map { possibleOracleS =>
|
||||
SchnorrDigitalSignature(outcome.aggregateNonce, possibleOracleS)
|
||||
}
|
||||
}
|
||||
|
||||
def computeOutcome(
|
||||
completedSig: ECDigitalSignature,
|
||||
possibleAdaptorSigs: Vector[(OracleOutcome, ECAdaptorSignature)]): Option[
|
||||
(SchnorrDigitalSignature, OracleOutcome)] = {
|
||||
val sigOpt = possibleAdaptorSigs.find { case (outcome, adaptorSig) =>
|
||||
possibleAdaptorSigs: Vector[(ECPublicKey, ECAdaptorSignature)]): Option[
|
||||
(FieldElement, ECPublicKey)] = {
|
||||
val sigOpt = possibleAdaptorSigs.find { case (adaptorPoint, adaptorSig) =>
|
||||
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) =>
|
||||
(sigFromOutcomeAndSigs(outcome, adaptorSig, completedSig).get, outcome)
|
||||
sigOpt.map { case (adaptorPoint, adaptorSig) =>
|
||||
(sigFromOutcomeAndSigs(adaptorPoint, adaptorSig, completedSig).get,
|
||||
adaptorPoint)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,9 +64,11 @@ object DLCUtil {
|
|||
offerFundingKey: ECPublicKey,
|
||||
acceptFundingKey: ECPublicKey,
|
||||
contractInfo: ContractInfo,
|
||||
localAdaptorSigs: Vector[(OracleOutcome, ECAdaptorSignature)],
|
||||
localAdaptorSigs: Vector[(ECPublicKey, ECAdaptorSignature)],
|
||||
cet: WitnessTransaction): Option[
|
||||
(SchnorrDigitalSignature, OracleOutcome)] = {
|
||||
val allAdaptorPoints = contractInfo.adaptorPoints
|
||||
|
||||
val cetSigs = cet.witness.head
|
||||
.asInstanceOf[P2WSHWitnessV0]
|
||||
.signatures
|
||||
|
@ -82,8 +79,8 @@ object DLCUtil {
|
|||
val outcomeValues = cet.outputs.map(_.value).sorted
|
||||
val totalCollateral = contractInfo.totalCollateral
|
||||
|
||||
val possibleOutcomes = contractInfo.allOutcomesAndPayouts
|
||||
.filter { case (_, amt) =>
|
||||
val possibleOutcomes = contractInfo.allOutcomesAndPayouts.zipWithIndex
|
||||
.filter { case ((_, amt), _) =>
|
||||
val amts = Vector(amt, totalCollateral - amt)
|
||||
.filter(_ >= Policy.dustThreshold)
|
||||
.sorted
|
||||
|
@ -95,7 +92,7 @@ object DLCUtil {
|
|||
Math.abs((amts.head - outcomeValues.head).satoshis.toLong) <= 1 && Math
|
||||
.abs((amts.last - outcomeValues.last).satoshis.toLong) <= 1
|
||||
}
|
||||
.map(_._1)
|
||||
.map { case (_, index) => allAdaptorPoints(index) }
|
||||
|
||||
val (offerCETSig, acceptCETSig) =
|
||||
if (offerFundingKey.hex.compareTo(acceptFundingKey.hex) > 0) {
|
||||
|
@ -114,6 +111,11 @@ object DLCUtil {
|
|||
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.transaction.{Transaction, WitnessTransaction}
|
||||
import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
|
||||
import org.bitcoins.core.util.Indexed
|
||||
import org.bitcoins.crypto.{AdaptorSign, ECPublicKey}
|
||||
|
||||
import scala.util.{Success, Try}
|
||||
|
@ -51,7 +52,7 @@ case class DLCExecutor(signer: DLCTxSigner) {
|
|||
}
|
||||
|
||||
val CETSignatures(outcomeSigs, refundSig) = cetSigs
|
||||
val msgs = outcomeSigs.map(_._1)
|
||||
val msgs = Indexed(outcomeSigs.map(_._1))
|
||||
val cets = cetsOpt match {
|
||||
case Some(cets) => cets
|
||||
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.
|
||||
*/
|
||||
def executeDLC(
|
||||
remoteCETInfos: Vector[(OracleOutcome, CETInfo)],
|
||||
remoteCETInfos: Vector[(ECPublicKey, CETInfo)],
|
||||
oracleSigs: Vector[OracleSignatures],
|
||||
fundingKey: AdaptorSign,
|
||||
remoteFundingPubKey: ECPublicKey,
|
||||
|
@ -141,7 +142,9 @@ object DLCExecutor {
|
|||
}
|
||||
|
||||
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 {
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
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.crypto.ECAdaptorSignature
|
||||
import org.bitcoins.crypto.{ECAdaptorSignature, ECPublicKey}
|
||||
|
||||
case class SetupDLC(
|
||||
fundingTx: Transaction,
|
||||
cets: Vector[(OracleOutcome, CETInfo)],
|
||||
cets: Vector[(ECPublicKey, CETInfo)],
|
||||
refundTx: WitnessTransaction) {
|
||||
cets.foreach { case (msg, cetInfo) =>
|
||||
require(
|
||||
|
@ -25,12 +24,12 @@ case class SetupDLC(
|
|||
s"RefundTx is not spending the funding tx, ${refundTx.inputs.head}"
|
||||
)
|
||||
|
||||
def getCETInfo(outcome: OracleOutcome): CETInfo = {
|
||||
cets.find(_._1 == outcome) match {
|
||||
def getCETInfo(adaptorPoint: ECPublicKey): CETInfo = {
|
||||
cets.find(_._1 == adaptorPoint) match {
|
||||
case Some((_, info)) => info
|
||||
case None =>
|
||||
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,
|
||||
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.tlv.{
|
||||
ContractInfoV0TLV,
|
||||
|
@ -13,6 +16,7 @@ import org.bitcoins.core.protocol.tlv.{
|
|||
TLVSerializable,
|
||||
UnsignedNumericOutcome
|
||||
}
|
||||
import org.bitcoins.core.util.Indexed
|
||||
import org.bitcoins.crypto.ECPublicKey
|
||||
|
||||
import scala.collection.immutable.HashMap
|
||||
|
@ -134,16 +138,16 @@ case class ContractInfo(
|
|||
|
||||
/** Maps adpator points to their corresponding OracleOutcomes (which correspond to CETs) */
|
||||
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 */
|
||||
lazy val outcomeMap: Map[OracleOutcome, (ECPublicKey, Satoshis, Satoshis)] = {
|
||||
val builder =
|
||||
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 adaptorPoint = outcome.sigPoint
|
||||
|
||||
builder.+=((outcome, (adaptorPoint, offerPayout, acceptPayout)))
|
||||
}
|
||||
|
@ -151,6 +155,13 @@ case class ContractInfo(
|
|||
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.
|
||||
*
|
||||
* Warning: This will return false if too many OracleSignatures are given.
|
||||
|
|
|
@ -275,10 +275,10 @@ object DLCMessage {
|
|||
def fromTLV(
|
||||
accept: DLCAcceptTLV,
|
||||
network: NetworkParameters,
|
||||
outcomes: Vector[OracleOutcome]): DLCAccept = {
|
||||
adaptorPoints: Vector[ECPublicKey]): DLCAccept = {
|
||||
val outcomeSigs = accept.cetSignatures match {
|
||||
case CETSignaturesV0TLV(sigs) =>
|
||||
outcomes.zip(sigs)
|
||||
adaptorPoints.zip(sigs)
|
||||
}
|
||||
|
||||
DLCAccept(
|
||||
|
@ -308,7 +308,7 @@ object DLCMessage {
|
|||
accept: DLCAcceptTLV,
|
||||
network: NetworkParameters,
|
||||
contractInfo: ContractInfo): DLCAccept = {
|
||||
fromTLV(accept, network, contractInfo.allOutcomes)
|
||||
fromTLV(accept, network, contractInfo.adaptorPoints)
|
||||
}
|
||||
|
||||
def fromTLV(accept: DLCAcceptTLV, offer: DLCOffer): DLCAccept = {
|
||||
|
@ -348,11 +348,11 @@ object DLCMessage {
|
|||
def fromTLV(
|
||||
sign: DLCSignTLV,
|
||||
fundingPubKey: ECPublicKey,
|
||||
outcomes: Vector[OracleOutcome],
|
||||
adaptorPoints: Vector[ECPublicKey],
|
||||
fundingOutPoints: Vector[TransactionOutPoint]): DLCSign = {
|
||||
val outcomeSigs = sign.cetSignatures match {
|
||||
case CETSignaturesV0TLV(sigs) =>
|
||||
outcomes.zip(sigs)
|
||||
adaptorPoints.zip(sigs)
|
||||
}
|
||||
|
||||
val sigs = sign.fundingSignatures match {
|
||||
|
@ -376,7 +376,7 @@ object DLCMessage {
|
|||
def fromTLV(sign: DLCSignTLV, offer: DLCOffer): DLCSign = {
|
||||
fromTLV(sign,
|
||||
offer.pubKeys.fundingKey,
|
||||
offer.contractInfo.allOutcomes,
|
||||
offer.contractInfo.adaptorPoints,
|
||||
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.transaction.TransactionOutPoint
|
||||
import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
|
||||
import org.bitcoins.core.util.SeqWrapper
|
||||
import org.bitcoins.crypto.ECAdaptorSignature
|
||||
import org.bitcoins.core.util.{Indexed, SeqWrapper}
|
||||
import org.bitcoins.crypto.{ECAdaptorSignature, ECPublicKey}
|
||||
|
||||
sealed trait DLCSignatures
|
||||
|
||||
|
@ -35,13 +35,19 @@ case class FundingSignatures(
|
|||
}
|
||||
|
||||
case class CETSignatures(
|
||||
outcomeSigs: Vector[(OracleOutcome, ECAdaptorSignature)],
|
||||
outcomeSigs: Vector[(ECPublicKey, ECAdaptorSignature)],
|
||||
refundSig: PartialSignature)
|
||||
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)
|
||||
|
||||
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
|
||||
.find(_._1 == key)
|
||||
.map(_._2)
|
||||
|
|
|
@ -25,6 +25,8 @@ sealed trait OracleOutcome {
|
|||
*/
|
||||
def outcome: DLCOutcomeType
|
||||
|
||||
def oraclesAndOutcomes: Vector[(SingleOracleInfo, DLCOutcomeType)]
|
||||
|
||||
protected def computeSigPoint: ECPublicKey
|
||||
|
||||
/** 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(_))
|
||||
}
|
||||
|
||||
override val oraclesAndOutcomes: Vector[(EnumSingleOracleInfo, EnumOutcome)] =
|
||||
oracles.map((_, outcome))
|
||||
|
||||
override lazy val aggregateNonce: SchnorrNonce = {
|
||||
oracles
|
||||
.map(_.aggregateNonce(outcome))
|
||||
|
@ -60,7 +65,7 @@ case class EnumOracleOutcome(
|
|||
/** Corresponds to a CET in an Numeric Outcome DLC where some set of `threshold`
|
||||
* oracles have each signed some NumericOutcome.
|
||||
*/
|
||||
case class NumericOracleOutcome(oraclesAndOutcomes: Vector[
|
||||
case class NumericOracleOutcome(override val oraclesAndOutcomes: Vector[
|
||||
(NumericSingleOracleInfo, UnsignedNumericOutcome)])
|
||||
extends OracleOutcome {
|
||||
|
||||
|
@ -103,5 +108,5 @@ object NumericOracleOutcome {
|
|||
}
|
||||
}
|
||||
|
||||
/** An oracle outcome and it's corresponding CET */
|
||||
case class OutcomeCETPair(outcome: OracleOutcome, wtx: WitnessTransaction)
|
||||
/** An adaptor point and it's corresponding CET */
|
||||
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.PSBT
|
||||
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.utxo._
|
||||
import org.bitcoins.crypto._
|
||||
|
@ -125,28 +125,28 @@ case class DLCTxSigner(
|
|||
}
|
||||
|
||||
/** Signs remote's Contract Execution Transaction (CET) for a given outcome */
|
||||
def signCET(outcome: OracleOutcome): ECAdaptorSignature = {
|
||||
signCETs(Vector(outcome)).head._2
|
||||
def signCET(adaptorPoint: ECPublicKey, index: Int): ECAdaptorSignature = {
|
||||
signCETs(Vector(Indexed(adaptorPoint, index))).head._2
|
||||
}
|
||||
|
||||
/** Signs remote's Contract Execution Transaction (CET) for a given outcomes */
|
||||
def buildAndSignCETs(outcomes: Vector[OracleOutcome]): Vector[
|
||||
(OracleOutcome, WitnessTransaction, ECAdaptorSignature)] = {
|
||||
val outcomesAndCETs = builder.buildCETsMap(outcomes)
|
||||
def buildAndSignCETs(adaptorPoints: Vector[Indexed[ECPublicKey]]): Vector[
|
||||
(ECPublicKey, WitnessTransaction, ECAdaptorSignature)] = {
|
||||
val outcomesAndCETs = builder.buildCETsMap(adaptorPoints)
|
||||
DLCTxSigner.buildAndSignCETs(outcomesAndCETs, cetSigningInfo, fundingKey)
|
||||
}
|
||||
|
||||
/** Signs remote's Contract Execution Transaction (CET) for a given outcomes */
|
||||
def signCETs(outcomes: Vector[OracleOutcome]): Vector[
|
||||
(OracleOutcome, ECAdaptorSignature)] = {
|
||||
buildAndSignCETs(outcomes).map { case (outcome, _, sig) =>
|
||||
def signCETs(adaptorPoints: Vector[Indexed[ECPublicKey]]): Vector[
|
||||
(ECPublicKey, ECAdaptorSignature)] = {
|
||||
buildAndSignCETs(adaptorPoints).map { case (outcome, _, sig) =>
|
||||
outcome -> sig
|
||||
}
|
||||
}
|
||||
|
||||
/** Signs remote's Contract Execution Transaction (CET) for a given outcomes and their corresponding CETs */
|
||||
def signGivenCETs(outcomesAndCETs: Vector[OutcomeCETPair]): Vector[
|
||||
(OracleOutcome, ECAdaptorSignature)] = {
|
||||
def signGivenCETs(outcomesAndCETs: Vector[AdaptorPointCETPair]): Vector[
|
||||
(ECPublicKey, ECAdaptorSignature)] = {
|
||||
DLCTxSigner.signCETs(outcomesAndCETs, cetSigningInfo, fundingKey)
|
||||
}
|
||||
|
||||
|
@ -154,12 +154,14 @@ case class DLCTxSigner(
|
|||
outcome: OracleOutcome,
|
||||
remoteAdaptorSig: ECAdaptorSignature,
|
||||
oracleSigs: Vector[OracleSignatures]): WitnessTransaction = {
|
||||
val index = builder.contractInfo.allOutcomes.indexOf(outcome)
|
||||
|
||||
DLCTxSigner.completeCET(
|
||||
outcome,
|
||||
cetSigningInfo,
|
||||
builder.fundingMultiSig,
|
||||
builder.buildFundingTx,
|
||||
builder.buildCET(outcome),
|
||||
builder.buildCET(outcome.sigPoint, index),
|
||||
remoteAdaptorSig,
|
||||
remoteFundingPubKey,
|
||||
oracleSigs
|
||||
|
@ -184,7 +186,8 @@ case class DLCTxSigner(
|
|||
|
||||
/** Creates all of this party's CETSignatures */
|
||||
def createCETSigs(): CETSignatures = {
|
||||
val cetSigs = signCETs(builder.contractInfo.allOutcomes)
|
||||
val adaptorPoints = builder.contractInfo.adaptorPointsIndexed
|
||||
val cetSigs = signCETs(adaptorPoints)
|
||||
val refundSig = signRefundTx
|
||||
|
||||
CETSignatures(cetSigs, refundSig)
|
||||
|
@ -193,19 +196,26 @@ case class DLCTxSigner(
|
|||
/** Creates CET signatures async */
|
||||
def createCETSigsAsync()(implicit
|
||||
ec: ExecutionContext): Future[CETSignatures] = {
|
||||
val outcomes = builder.contractInfo.allOutcomes
|
||||
val adaptorPoints = builder.contractInfo.adaptorPointsIndexed
|
||||
//divide and conquer
|
||||
|
||||
val computeBatchFn: Vector[OracleOutcome] => Future[
|
||||
Vector[(OracleOutcome, ECAdaptorSignature)]] = {
|
||||
case outcomes: Vector[OracleOutcome] =>
|
||||
//we want a batch size of at least 1
|
||||
val size =
|
||||
Math.max(adaptorPoints.length / Runtime.getRuntime.availableProcessors(),
|
||||
1)
|
||||
|
||||
val computeBatchFn: Vector[Indexed[ECPublicKey]] => Future[
|
||||
Vector[(ECPublicKey, ECAdaptorSignature)]] = {
|
||||
adaptorPoints: Vector[Indexed[ECPublicKey]] =>
|
||||
Future {
|
||||
signCETs(outcomes)
|
||||
signCETs(adaptorPoints)
|
||||
}
|
||||
}
|
||||
|
||||
val cetSigsF: Future[Vector[(OracleOutcome, ECAdaptorSignature)]] = {
|
||||
FutureUtil.batchAndParallelExecute(elements = outcomes,
|
||||
f = computeBatchFn)
|
||||
val cetSigsF: Future[Vector[(ECPublicKey, ECAdaptorSignature)]] = {
|
||||
FutureUtil.batchAndParallelExecute(elements = adaptorPoints,
|
||||
f = computeBatchFn,
|
||||
batchSize = size)
|
||||
}.map(_.flatten)
|
||||
|
||||
for {
|
||||
|
@ -216,29 +226,31 @@ case class DLCTxSigner(
|
|||
|
||||
/** Creates all of this party's CETSignatures */
|
||||
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 refundSig = signRefundTx
|
||||
|
||||
(CETSignatures(msgs.zip(sigs), refundSig), cets)
|
||||
}
|
||||
|
||||
/** The equivalent of [[createCETsAndCETSigs()]] but async */
|
||||
def createCETsAndCETSigsAsync()(implicit
|
||||
ec: ExecutionContext): Future[(CETSignatures, Vector[WitnessTransaction])] = {
|
||||
val outcomes = builder.contractInfo.allOutcomes
|
||||
val fn = { outcomes: Vector[OracleOutcome] =>
|
||||
val adaptorPoints = builder.contractInfo.adaptorPointsIndexed
|
||||
val fn = { adaptorPoints: Vector[Indexed[ECPublicKey]] =>
|
||||
Future {
|
||||
buildAndSignCETs(outcomes)
|
||||
buildAndSignCETs(adaptorPoints)
|
||||
}
|
||||
}
|
||||
val cetsAndSigsF: Future[Vector[
|
||||
Vector[(OracleOutcome, WitnessTransaction, ECAdaptorSignature)]]] = {
|
||||
FutureUtil.batchAndParallelExecute[OracleOutcome,
|
||||
val cetsAndSigsF: Future[
|
||||
Vector[Vector[(ECPublicKey, WitnessTransaction, ECAdaptorSignature)]]] = {
|
||||
FutureUtil.batchAndParallelExecute[Indexed[ECPublicKey],
|
||||
Vector[(
|
||||
OracleOutcome,
|
||||
ECPublicKey,
|
||||
WitnessTransaction,
|
||||
ECAdaptorSignature)]](elements =
|
||||
outcomes,
|
||||
ECAdaptorSignature)]](
|
||||
elements = adaptorPoints,
|
||||
f = fn)
|
||||
}
|
||||
|
||||
|
@ -252,7 +264,8 @@ case class DLCTxSigner(
|
|||
}
|
||||
|
||||
/** 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 refundSig = signRefundTx
|
||||
|
||||
|
@ -296,38 +309,37 @@ object DLCTxSigner {
|
|||
}
|
||||
|
||||
def signCET(
|
||||
outcome: OracleOutcome,
|
||||
sigPoint: ECPublicKey,
|
||||
cet: WitnessTransaction,
|
||||
cetSigningInfo: ECSignatureParams[P2WSHV0InputInfo],
|
||||
fundingKey: AdaptorSign): ECAdaptorSignature = {
|
||||
signCETs(Vector(OutcomeCETPair(outcome, cet)),
|
||||
signCETs(Vector(AdaptorPointCETPair(sigPoint, cet)),
|
||||
cetSigningInfo,
|
||||
fundingKey).head._2
|
||||
}
|
||||
|
||||
def signCETs(
|
||||
outcomesAndCETs: Vector[OutcomeCETPair],
|
||||
outcomesAndCETs: Vector[AdaptorPointCETPair],
|
||||
cetSigningInfo: ECSignatureParams[P2WSHV0InputInfo],
|
||||
fundingKey: AdaptorSign): Vector[(OracleOutcome, ECAdaptorSignature)] = {
|
||||
fundingKey: AdaptorSign): Vector[(ECPublicKey, ECAdaptorSignature)] = {
|
||||
buildAndSignCETs(outcomesAndCETs, cetSigningInfo, fundingKey).map {
|
||||
case (outcome, _, sig) => outcome -> sig
|
||||
}
|
||||
}
|
||||
|
||||
def buildAndSignCETs(
|
||||
outcomesAndCETs: Vector[OutcomeCETPair],
|
||||
outcomesAndCETs: Vector[AdaptorPointCETPair],
|
||||
cetSigningInfo: ECSignatureParams[P2WSHV0InputInfo],
|
||||
fundingKey: AdaptorSign): Vector[
|
||||
(OracleOutcome, WitnessTransaction, ECAdaptorSignature)] = {
|
||||
outcomesAndCETs.map { case OutcomeCETPair(outcome, cet) =>
|
||||
val adaptorPoint = outcome.sigPoint
|
||||
(ECPublicKey, WitnessTransaction, ECAdaptorSignature)] = {
|
||||
outcomesAndCETs.map { case AdaptorPointCETPair(sigPoint, cet) =>
|
||||
val hashToSign =
|
||||
TransactionSignatureSerializer.hashForSignature(cet,
|
||||
cetSigningInfo,
|
||||
HashType.sigHashAll)
|
||||
|
||||
val adaptorSig = fundingKey.adaptorSign(adaptorPoint, hashToSign.bytes)
|
||||
(outcome, cet, adaptorSig)
|
||||
val adaptorSig = fundingKey.adaptorSign(sigPoint, hashToSign.bytes)
|
||||
(sigPoint, cet, adaptorSig)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -371,7 +383,7 @@ object DLCTxSigner {
|
|||
.map(_.asInstanceOf[WitnessTransaction])
|
||||
|
||||
cetT match {
|
||||
case Success(cet) => cet.asInstanceOf[WitnessTransaction]
|
||||
case Success(cet) => cet
|
||||
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.models.{
|
||||
DLCFundingInput,
|
||||
FundingSignatures,
|
||||
OracleOutcome
|
||||
FundingSignatures
|
||||
}
|
||||
import org.bitcoins.core.protocol.transaction.{Transaction, WitnessTransaction}
|
||||
import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
|
||||
import org.bitcoins.core.psbt.PSBT
|
||||
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 scodec.bits.ByteVector
|
||||
|
||||
|
@ -37,15 +36,17 @@ case class DLCSignatureVerifier(builder: DLCTxBuilder, isInitiator: Boolean) {
|
|||
}
|
||||
|
||||
/** 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) {
|
||||
builder.acceptFundingKey
|
||||
} else {
|
||||
builder.offerFundingKey
|
||||
}
|
||||
val cet = builder.buildCET(outcome)
|
||||
val cet = builder.buildCET(adaptorPoint)
|
||||
|
||||
DLCSignatureVerifier.validateCETSignature(outcome,
|
||||
DLCSignatureVerifier.validateCETSignature(adaptorPoint.element,
|
||||
sig,
|
||||
remoteFundingPubKey,
|
||||
fundingTx,
|
||||
|
@ -53,14 +54,14 @@ case class DLCSignatureVerifier(builder: DLCTxBuilder, isInitiator: Boolean) {
|
|||
cet)
|
||||
}
|
||||
|
||||
def verifyCETSigs(sigs: Vector[(OracleOutcome, ECAdaptorSignature)])(implicit
|
||||
ec: ExecutionContext): Future[Boolean] = {
|
||||
def verifyCETSigs(sigs: Vector[(Indexed[ECPublicKey], ECAdaptorSignature)])(
|
||||
implicit ec: ExecutionContext): Future[Boolean] = {
|
||||
val correctNumberOfSigs =
|
||||
sigs.size >= builder.contractInfo.allOutcomes.length
|
||||
|
||||
def runVerify(
|
||||
outcomeSigs: Vector[(OracleOutcome, ECAdaptorSignature)]): Future[
|
||||
Boolean] = {
|
||||
outcomeSigs: Vector[
|
||||
(Indexed[ECPublicKey], ECAdaptorSignature)]): Future[Boolean] = {
|
||||
Future {
|
||||
outcomeSigs.foldLeft(true) { case (ret, (outcome, sig)) =>
|
||||
ret && verifyCETSig(outcome, sig)
|
||||
|
@ -89,15 +90,13 @@ case class DLCSignatureVerifier(builder: DLCTxBuilder, isInitiator: Boolean) {
|
|||
object DLCSignatureVerifier {
|
||||
|
||||
def validateCETSignature(
|
||||
outcome: OracleOutcome,
|
||||
adaptorPoint: ECPublicKey,
|
||||
sig: ECAdaptorSignature,
|
||||
remoteFundingPubKey: ECPublicKey,
|
||||
fundingTx: Transaction,
|
||||
fundOutputIndex: Int,
|
||||
cet: WitnessTransaction
|
||||
): Boolean = {
|
||||
val adaptorPoint = outcome.sigPoint
|
||||
|
||||
val sigComponent = WitnessTxSigComponentRaw(
|
||||
transaction = cet,
|
||||
inputIndex = UInt32.zero,
|
||||
|
|
|
@ -103,12 +103,18 @@ object FutureUtil {
|
|||
elements: Vector[T],
|
||||
f: Vector[T] => Future[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 execute: Vector[Future[U]] = batches.map(b => f(b))
|
||||
val doneF = Future.sequence(execute)
|
||||
doneF
|
||||
}
|
||||
}
|
||||
|
||||
/** Same as [[batchAndParallelExecute()]], but computes the batchSize based on the
|
||||
* number of available processors on your machine
|
||||
|
|
|
@ -102,6 +102,10 @@ sealed trait PublicKey extends NetworkElement {
|
|||
fromBytes(x.+:(leadByte))
|
||||
}
|
||||
}
|
||||
|
||||
override def hashCode: Int = {
|
||||
bytes.hashCode
|
||||
}
|
||||
}
|
||||
|
||||
/** 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] = {
|
||||
MappedColumnType.base[UInt64, BigDecimal](
|
||||
MappedColumnType.base[UInt64, String](
|
||||
{ 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
|
||||
{ bigDec: BigDecimal =>
|
||||
UInt64(bigDec.toBigIntExact.get)
|
||||
}
|
||||
UInt64.fromHex
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ import org.bitcoins.core.protocol.tlv.{
|
|||
}
|
||||
import org.bitcoins.core.protocol.transaction._
|
||||
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.crypto._
|
||||
import org.bitcoins.testkitcore.dlc.{DLCFeeTestUtil, DLCTest, TestDLCClient}
|
||||
|
@ -334,6 +334,39 @@ class DLCClientTest extends BitcoinSJvmTest with DLCTest {
|
|||
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 {
|
||||
val (offerClient, acceptClient, outcomes) =
|
||||
constructDLCClients(numOutcomesOrDigits = 3,
|
||||
|
@ -360,15 +393,16 @@ class DLCClientTest extends BitcoinSJvmTest with DLCTest {
|
|||
val oracleSig = genEnumOracleSignature(oracleInfo, outcome.outcome)
|
||||
|
||||
assertThrows[RuntimeException] {
|
||||
offerClient.dlcTxSigner.completeCET(oracleOutcome,
|
||||
badAcceptCETSigs(oracleOutcome),
|
||||
offerClient.dlcTxSigner.completeCET(
|
||||
oracleOutcome,
|
||||
badAcceptCETSigs(oracleOutcome.sigPoint),
|
||||
Vector(oracleSig))
|
||||
}
|
||||
|
||||
assertThrows[RuntimeException] {
|
||||
acceptClient.dlcTxSigner
|
||||
.completeCET(oracleOutcome,
|
||||
badOfferCETSigs(oracleOutcome),
|
||||
badOfferCETSigs(oracleOutcome.sigPoint),
|
||||
Vector(oracleSig))
|
||||
}
|
||||
}
|
||||
|
@ -381,29 +415,25 @@ class DLCClientTest extends BitcoinSJvmTest with DLCTest {
|
|||
acceptClient.dlcTxSigner.completeRefundTx(badOfferCETSigs.refundSig)
|
||||
}
|
||||
|
||||
outcomes.foreach { outcomeUncast =>
|
||||
outcomes.zipWithIndex.foreach { case (outcomeUncast, index) =>
|
||||
val outcome = EnumOracleOutcome(
|
||||
Vector(offerClient.offer.oracleInfo.asInstanceOf[EnumSingleOracleInfo]),
|
||||
outcomeUncast.asInstanceOf[EnumOutcome])
|
||||
val adaptorPoint = Indexed(outcome.sigPoint, index)
|
||||
|
||||
assert(offerVerifier.verifyCETSig(outcome, acceptCETSigs(outcome)))
|
||||
assert(acceptVerifier.verifyCETSig(outcome, offerCETSigs(outcome)))
|
||||
}
|
||||
assert(offerVerifier.verifyRefundSig(acceptCETSigs.refundSig))
|
||||
assert(offerVerifier.verifyRefundSig(offerCETSigs.refundSig))
|
||||
assert(acceptVerifier.verifyRefundSig(offerCETSigs.refundSig))
|
||||
assert(acceptVerifier.verifyRefundSig(acceptCETSigs.refundSig))
|
||||
assert(
|
||||
!offerVerifier.verifyCETSig(adaptorPoint,
|
||||
badAcceptCETSigs(outcome.sigPoint)))
|
||||
assert(
|
||||
!acceptVerifier.verifyCETSig(adaptorPoint,
|
||||
badOfferCETSigs(outcome.sigPoint)))
|
||||
|
||||
outcomes.foreach { outcomeUncast =>
|
||||
val outcome = EnumOracleOutcome(
|
||||
Vector(offerClient.offer.oracleInfo.asInstanceOf[EnumSingleOracleInfo]),
|
||||
outcomeUncast.asInstanceOf[EnumOutcome])
|
||||
|
||||
assert(!offerVerifier.verifyCETSig(outcome, badAcceptCETSigs(outcome)))
|
||||
assert(!acceptVerifier.verifyCETSig(outcome, badOfferCETSigs(outcome)))
|
||||
|
||||
assert(!offerVerifier.verifyCETSig(outcome, offerCETSigs(outcome)))
|
||||
assert(!acceptVerifier.verifyCETSig(outcome, acceptCETSigs(outcome)))
|
||||
assert(
|
||||
!offerVerifier.verifyCETSig(adaptorPoint,
|
||||
offerCETSigs(outcome.sigPoint)))
|
||||
assert(
|
||||
!acceptVerifier.verifyCETSig(adaptorPoint,
|
||||
acceptCETSigs(outcome.sigPoint)))
|
||||
}
|
||||
assert(!offerVerifier.verifyRefundSig(badAcceptCETSigs.refundSig))
|
||||
assert(!offerVerifier.verifyRefundSig(badOfferCETSigs.refundSig))
|
||||
|
@ -411,6 +441,36 @@ class DLCClientTest extends BitcoinSJvmTest with DLCTest {
|
|||
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(
|
||||
offerSetup: SetupDLC,
|
||||
dlcOffer: TestDLCClient,
|
||||
|
|
|
@ -54,9 +54,10 @@ class SetupDLCTest extends BitcoinSJvmTest {
|
|||
refundTx: WitnessTransaction = validRefundTx): SetupDLC = {
|
||||
SetupDLC(
|
||||
fundingTx = fundingTx,
|
||||
cets = Vector(
|
||||
EnumOracleOutcome(Vector(oracleInfo), EnumOutcome("WIN")) -> cet0,
|
||||
EnumOracleOutcome(Vector(oracleInfo), EnumOutcome("LOSE")) -> cet1),
|
||||
cets = Vector(EnumOracleOutcome(Vector(oracleInfo),
|
||||
EnumOutcome("WIN")).sigPoint -> cet0,
|
||||
EnumOracleOutcome(Vector(oracleInfo),
|
||||
EnumOutcome("LOSE")).sigPoint -> cet1),
|
||||
refundTx = refundTx
|
||||
)
|
||||
}
|
||||
|
|
|
@ -207,7 +207,7 @@ object DLCTLVGen {
|
|||
ECPublicKey.freshPublicKey): CETSignatures = {
|
||||
CETSignatures(
|
||||
outcomes.map(outcome =>
|
||||
EnumOracleOutcome(Vector(oracleInfo), outcome) -> adaptorSig),
|
||||
EnumOracleOutcome(Vector(oracleInfo), outcome).sigPoint -> adaptorSig),
|
||||
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.script.crypto.HashType
|
||||
import org.bitcoins.core.util.Indexed
|
||||
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
|
||||
import org.bitcoins.core.wallet.utxo.{
|
||||
ConditionalPath,
|
||||
|
@ -186,12 +187,13 @@ case class ValidTestInputs(
|
|||
def buildTransactions: DLCTransactions = {
|
||||
val builder = this.builder
|
||||
val fundingTx = builder.buildFundingTx
|
||||
val cets =
|
||||
val adaptorPoints =
|
||||
params.contractInfo
|
||||
.map(_.preImage)
|
||||
.map(EnumOutcome.apply)
|
||||
.map(outcome => EnumOracleOutcome(Vector(params.oracleInfo), outcome))
|
||||
.map(builder.buildCET)
|
||||
.map(_.sigPoint)
|
||||
val cets = builder.buildCETs(Indexed(adaptorPoints))
|
||||
val refundTx = builder.buildRefundTx
|
||||
|
||||
DLCTransactions(fundingTx, cets, refundTx)
|
||||
|
|
|
@ -246,7 +246,7 @@ object DLCTxGen {
|
|||
EnumOracleOutcome(Vector(inputs.params.oracleInfo),
|
||||
EnumOutcome(outcomeStr))
|
||||
|
||||
val accpetCETSigs = acceptSigner.createCETSigs()
|
||||
val acceptCETSigs = acceptSigner.createCETSigs()
|
||||
val offerCETSigs = offerSigner.createCETSigs()
|
||||
|
||||
for {
|
||||
|
@ -256,23 +256,23 @@ object DLCTxGen {
|
|||
|
||||
signedFundingTx <- acceptSigner.completeFundingTx(offerFundingSigs)
|
||||
} yield {
|
||||
val signedRefundTx = offerSigner.completeRefundTx(accpetCETSigs.refundSig)
|
||||
val signedRefundTx = offerSigner.completeRefundTx(acceptCETSigs.refundSig)
|
||||
|
||||
val offerSignedCET = offerSigner.completeCET(
|
||||
outcome,
|
||||
accpetCETSigs(outcome),
|
||||
acceptCETSigs(outcome.sigPoint),
|
||||
Vector(
|
||||
EnumOracleSignature(inputs.params.oracleInfo,
|
||||
inputs.params.oracleSignature)))
|
||||
|
||||
val acceptSignedCET = acceptSigner.completeCET(
|
||||
outcome,
|
||||
offerCETSigs(outcome),
|
||||
offerCETSigs(outcome.sigPoint),
|
||||
Vector(
|
||||
EnumOracleSignature(inputs.params.oracleInfo,
|
||||
inputs.params.oracleSignature)))
|
||||
|
||||
val accept = acceptWithoutSigs.withSigs(accpetCETSigs)
|
||||
val accept = acceptWithoutSigs.withSigs(acceptCETSigs)
|
||||
|
||||
val contractId = fundingTx.txIdBE.bytes.xor(accept.tempContractId.bytes)
|
||||
val sign = DLCSign(offerCETSigs, offerFundingSigs, contractId)
|
||||
|
|
|
@ -98,7 +98,7 @@ case class TestDLCClient(
|
|||
}
|
||||
cetSigs =
|
||||
dlcTxSigner.createCETSigs(setupDLCWithoutFundingTxSigs.cets.map {
|
||||
case (msg, info) => OutcomeCETPair(msg, info.tx)
|
||||
case (msg, info) => AdaptorPointCETPair(msg, info.tx)
|
||||
})
|
||||
localFundingSigs <- Future.fromTry {
|
||||
dlcTxSigner.signFundingTx()
|
||||
|
|
|
@ -3,10 +3,10 @@ package org.bitcoins.testkit
|
|||
import com.typesafe.config._
|
||||
import org.bitcoins.dlc.oracle.config.DLCOracleAppConfig
|
||||
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.util.FileUtil
|
||||
import org.bitcoins.testkitcore.Implicits.GeneratorOps
|
||||
import org.bitcoins.testkitcore.gen.{NumberGenerator, StringGenerators}
|
||||
|
||||
import java.nio.file._
|
||||
import scala.concurrent.ExecutionContext
|
||||
|
@ -149,9 +149,10 @@ object BitcoinSTestAppConfig {
|
|||
case object Node extends ProjectType
|
||||
case object Chain extends ProjectType
|
||||
case object Oracle extends ProjectType
|
||||
case object DLC 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
|
||||
|
@ -178,6 +179,7 @@ object BitcoinSTestAppConfig {
|
|||
case ProjectType.Chain => "chain"
|
||||
case ProjectType.Node => "node"
|
||||
case ProjectType.Oracle => "oracle"
|
||||
case ProjectType.DLC => "dlc"
|
||||
case ProjectType.Test => "test"
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue