Pulled down all remaining non-wallet non-gui code on adaptor-dlc (#3101)

This commit is contained in:
Nadav Kohen 2021-05-18 05:29:46 -06:00 committed by GitHub
parent a0453ad660
commit ac3bae403b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 507 additions and 205 deletions

View file

@ -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}

View file

@ -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

View file

@ -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)

View file

@ -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))

View file

@ -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)
}
}
} }

View file

@ -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,

View file

@ -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)
}
}
}

View file

@ -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)
}
} }
} }

View file

@ -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 {

View file

@ -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")
} }
} }
} }

View file

@ -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.

View file

@ -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))
} }

View file

@ -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)

View file

@ -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)

View file

@ -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
} }
} }

View file

@ -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,

View file

@ -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

View file

@ -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). */

View file

@ -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)
}
) )
} }

View file

@ -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,

View file

@ -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
) )
} }

View file

@ -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))
} }

View file

@ -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)

View file

@ -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)

View file

@ -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()

View file

@ -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"
} }