2024 11 15 partialsig typeparam (#5770)

* Add DigitalSignature type param to PartialSignature

* Remove isDummySignature
This commit is contained in:
Chris Stewart 2024-11-18 09:15:33 -06:00 committed by GitHub
parent bb0e40f05b
commit 80be2f5989
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 298 additions and 213 deletions

View File

@ -1,14 +1,14 @@
package org.bitcoins.commons.jsonmodels package org.bitcoins.commons.jsonmodels
import org.bitcoins.commons.jsonmodels.SerializedTransaction._ import org.bitcoins.commons.jsonmodels.SerializedTransaction.*
import org.bitcoins.commons.serializers.JsonSerializers._ import org.bitcoins.commons.serializers.JsonSerializers.*
import org.bitcoins.core.crypto.ExtPublicKey import org.bitcoins.core.crypto.ExtPublicKey
import org.bitcoins.core.number.UInt32 import org.bitcoins.core.number.UInt32
import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
import org.bitcoins.core.psbt._ import org.bitcoins.core.psbt.*
import org.bitcoins.core.script.constant.ScriptToken import org.bitcoins.core.script.constant.ScriptToken
import org.bitcoins.crypto.HashType import org.bitcoins.crypto.{DigitalSignature, HashType}
import play.api.libs.json._ import play.api.libs.json.*
import scodec.bits.ByteVector import scodec.bits.ByteVector
case class SerializedPSBT( case class SerializedPSBT(
@ -29,7 +29,7 @@ case class SerializedPSBTGlobalMap(
case class SerializedPSBTInputMap( case class SerializedPSBTInputMap(
nonWitnessUtxo: Option[SerializedTransaction], nonWitnessUtxo: Option[SerializedTransaction],
witnessUtxo: Option[SerializedTransactionOutput], witnessUtxo: Option[SerializedTransactionOutput],
signatures: Option[Vector[PartialSignature]], signatures: Option[Vector[PartialSignature[DigitalSignature]]],
sigHashType: Option[HashType], sigHashType: Option[HashType],
redeemScript: Option[Vector[ScriptToken]], redeemScript: Option[Vector[ScriptToken]],
witScript: Option[Vector[ScriptToken]], witScript: Option[Vector[ScriptToken]],
@ -68,7 +68,7 @@ object SerializedPSBT {
val witnessUtxo = input.witnessUTXOOpt.map(rec => val witnessUtxo = input.witnessUTXOOpt.map(rec =>
decodeTransactionOutput(rec.witnessUTXO, index)) decodeTransactionOutput(rec.witnessUTXO, index))
val sigs = input.partialSignatures val sigs = input.partialSignatures[DigitalSignature]
val sigsOpt = if (sigs.nonEmpty) Some(sigs) else None val sigsOpt = if (sigs.nonEmpty) Some(sigs) else None
val hashType = input.sigHashTypeOpt.map(_.hashType) val hashType = input.sigHashTypeOpt.map(_.hashType)
val redeemScript = input.redeemScriptOpt.map(_.redeemScript.asm.toVector) val redeemScript = input.redeemScriptOpt.map(_.redeemScript.asm.toVector)

View File

@ -848,8 +848,10 @@ object JsonSerializers {
implicit val serializedPSBTGlobalWrites: Writes[SerializedPSBTGlobalMap] = implicit val serializedPSBTGlobalWrites: Writes[SerializedPSBTGlobalMap] =
Json.writes[SerializedPSBTGlobalMap] Json.writes[SerializedPSBTGlobalMap]
implicit val serializedPSBTInputWrites: Writes[SerializedPSBTInputMap] = implicit val serializedPSBTInputWrites: Writes[SerializedPSBTInputMap] = {
import JsonWriters.PartialSignatureWrites
Json.writes[SerializedPSBTInputMap] Json.writes[SerializedPSBTInputMap]
}
implicit val serializedPSBTOutputWrites: Writes[SerializedPSBTOutputMap] = implicit val serializedPSBTOutputWrites: Writes[SerializedPSBTOutputMap] =
Json.writes[SerializedPSBTOutputMap] Json.writes[SerializedPSBTOutputMap]

View File

@ -257,9 +257,10 @@ object JsonWriters {
} }
implicit object PartialSignatureWrites implicit object PartialSignatureWrites
extends Writes[InputPSBTRecord.PartialSignature] { extends Writes[InputPSBTRecord.PartialSignature[DigitalSignature]] {
override def writes(o: InputPSBTRecord.PartialSignature): JsValue = override def writes(
o: InputPSBTRecord.PartialSignature[DigitalSignature]): JsValue =
JsObject( JsObject(
Seq( Seq(
("pubkey", JsString(o.pubKey.hex)), ("pubkey", JsString(o.pubKey.hex)),

View File

@ -140,7 +140,7 @@ object Picklers {
: ReadWriter[SchnorrDigitalSignature] = : ReadWriter[SchnorrDigitalSignature] =
readwriter[String].bimap(_.hex, SchnorrDigitalSignature.fromHex) readwriter[String].bimap(_.hex, SchnorrDigitalSignature.fromHex)
implicit val partialSignaturePickler: ReadWriter[PartialSignature] = implicit val partialSignaturePickler: ReadWriter[PartialSignature[?]] =
readwriter[String].bimap(_.hex, PartialSignature.fromHex) readwriter[String].bimap(_.hex, PartialSignature.fromHex)
implicit val lnMessageDLCOfferTLVPickler: ReadWriter[LnMessage[DLCOfferTLV]] = implicit val lnMessageDLCOfferTLVPickler: ReadWriter[LnMessage[DLCOfferTLV]] =

View File

@ -291,11 +291,11 @@ object CliReaders {
str => SchnorrDigitalSignature.fromHex(str.trim) str => SchnorrDigitalSignature.fromHex(str.trim)
} }
implicit val partialSigReads: Read[PartialSignature] = implicit val partialSigReads: Read[PartialSignature[DigitalSignature]] =
new Read[PartialSignature] { new Read[PartialSignature[DigitalSignature]] {
override def arity: Int = 1 override def arity: Int = 1
override def reads: String => PartialSignature = override def reads: String => PartialSignature[DigitalSignature] =
PartialSignature.fromHex PartialSignature.fromHex
} }

View File

@ -554,7 +554,8 @@ class WalletRpcTest extends BitcoindFixturesCachedPairNewest {
privKey, privKey,
HashType.sigHashAll HashType.sigHashAll
), ),
transaction transaction,
privKey.signLowRWithHashType
) )
signedTx match { signedTx match {

View File

@ -212,7 +212,7 @@ object DLCTLVGen {
def partialSig( def partialSig(
pubKey: ECPublicKey = ECPublicKey.freshPublicKey, pubKey: ECPublicKey = ECPublicKey.freshPublicKey,
sigHashByte: Boolean = true sigHashByte: Boolean = true
): PartialSignature = { ): PartialSignature[ECDigitalSignature] = {
PartialSignature(pubKey, ecdsaSig(sigHashByte)) PartialSignature(pubKey, ecdsaSig(sigHashByte))
} }
@ -236,7 +236,7 @@ object DLCTLVGen {
def refundSigs( def refundSigs(
fundingPubKey: ECPublicKey = ECPublicKey.freshPublicKey fundingPubKey: ECPublicKey = ECPublicKey.freshPublicKey
): PartialSignature = { ): PartialSignature[ECDigitalSignature] = {
partialSig(fundingPubKey, sigHashByte = false) partialSig(fundingPubKey, sigHashByte = false)
} }
@ -367,7 +367,7 @@ object DLCTLVGen {
changeAddress: BitcoinAddress = address(), changeAddress: BitcoinAddress = address(),
changeSerialId: UInt64 = DLCMessage.genSerialId(), changeSerialId: UInt64 = DLCMessage.genSerialId(),
cetSignatures: CETSignatures = cetSigs(), cetSignatures: CETSignatures = cetSigs(),
refundSignatures: PartialSignature = refundSigs(), refundSignatures: PartialSignature[ECDigitalSignature] = refundSigs(),
tempContractId: Sha256Digest = hash() tempContractId: Sha256Digest = hash()
): DLCAccept = { ): DLCAccept = {
DLCAccept( DLCAccept(
@ -393,7 +393,7 @@ object DLCTLVGen {
changeAddress: BitcoinAddress = address(), changeAddress: BitcoinAddress = address(),
changeSerialId: UInt64 = DLCMessage.genSerialId(), changeSerialId: UInt64 = DLCMessage.genSerialId(),
cetSignatures: CETSignatures = cetSigs(), cetSignatures: CETSignatures = cetSigs(),
refundSignatures: PartialSignature = refundSigs(), refundSignatures: PartialSignature[ECDigitalSignature] = refundSigs(),
tempContractId: Sha256Digest = hash() tempContractId: Sha256Digest = hash()
): DLCAcceptTLV = { ): DLCAcceptTLV = {
dlcAccept( dlcAccept(
@ -499,7 +499,7 @@ object DLCTLVGen {
def dlcSign( def dlcSign(
cetSignatures: CETSignatures = cetSigs(), cetSignatures: CETSignatures = cetSigs(),
refundSignatures: PartialSignature = refundSigs(), refundSignatures: PartialSignature[ECDigitalSignature] = refundSigs(),
fundingSignatures: FundingSignatures = fundingSigs(), fundingSignatures: FundingSignatures = fundingSigs(),
contractId: ByteVector = hash().bytes contractId: ByteVector = hash().bytes
): DLCSign = { ): DLCSign = {
@ -508,7 +508,7 @@ object DLCTLVGen {
def dlcSignTLV( def dlcSignTLV(
cetSignatures: CETSignatures = cetSigs(), cetSignatures: CETSignatures = cetSigs(),
refundSignatures: PartialSignature = refundSigs(), refundSignatures: PartialSignature[ECDigitalSignature] = refundSigs(),
fundingSignatures: FundingSignatures = fundingSigs(), fundingSignatures: FundingSignatures = fundingSigs(),
contractId: ByteVector = hash().bytes contractId: ByteVector = hash().bytes
): DLCSignTLV = { ): DLCSignTLV = {
@ -522,7 +522,7 @@ object DLCTLVGen {
def dlcSignParsingTestVector( def dlcSignParsingTestVector(
cetSignatures: CETSignatures = cetSigs(), cetSignatures: CETSignatures = cetSigs(),
refundSignatures: PartialSignature = refundSigs(), refundSignatures: PartialSignature[ECDigitalSignature] = refundSigs(),
fundingSignatures: FundingSignatures = fundingSigs(), fundingSignatures: FundingSignatures = fundingSigs(),
contractId: ByteVector = hash().bytes contractId: ByteVector = hash().bytes
): DLCParsingTestVector = { ): DLCParsingTestVector = {

View File

@ -129,7 +129,7 @@ class TransactionSignatureCreatorTest extends BitcoinSJvmTest {
TransactionSignatureCreator.createSig( TransactionSignatureCreator.createSig(
transaction, transaction,
signingInfo, signingInfo,
privateKey, privateKey.signLowRWithHashType,
HashType.sigHashAll HashType.sigHashAll
) )
txSignature.r must be(expectedSig.r) txSignature.r must be(expectedSig.r)

View File

@ -45,7 +45,7 @@ class DLCMessageTest extends BitcoinSJvmTest {
Vector(EnumOutcome(dummyStr)) Vector(EnumOutcome(dummyStr))
) )
val dummySig: PartialSignature = val dummySig: PartialSignature[ECDigitalSignature] =
PartialSignature(dummyPubKey, ECDigitalSignature.empty) PartialSignature(dummyPubKey, ECDigitalSignature.empty)
it must "not allow a negative collateral for a DLCOffer" in { it must "not allow a negative collateral for a DLCOffer" in {

View File

@ -110,7 +110,8 @@ class SignerTest extends BitcoinSUnitTest {
val keyAndSig = val keyAndSig =
BitcoinSigner.signSingle( BitcoinSigner.signSingle(
singleInfo, singleInfo,
unsignedTx unsignedTx,
singleInfo.signer.signLowRWithHashType
) )
keyAndSig.signature keyAndSig.signature
@ -235,7 +236,7 @@ class SignerTest extends BitcoinSUnitTest {
changeSPK changeSPK
) )
val singleSigs: Vector[Vector[PartialSignature]] = { val singleSigs: Vector[Vector[PartialSignature[ECDigitalSignature]]] = {
val singleInfosVec: Vector[Vector[ECSignatureParams[InputInfo]]] = val singleInfosVec: Vector[Vector[ECSignatureParams[InputInfo]]] =
creditingTxsInfos.toVector.map(_.toSingles) creditingTxsInfos.toVector.map(_.toSingles)
singleInfosVec.map { singleInfos => singleInfosVec.map { singleInfos =>
@ -248,7 +249,9 @@ class SignerTest extends BitcoinSUnitTest {
unsignedTx.lockTime, unsignedTx.lockTime,
EmptyWitness.fromInputs(unsignedTx.inputs) EmptyWitness.fromInputs(unsignedTx.inputs)
) )
BitcoinSigner.signSingle(singleInfo, wtx) BitcoinSigner.signSingle(singleInfo,
wtx,
singleInfo.signer.signLowRWithHashType)
} }
} }

View File

@ -10,7 +10,6 @@ import org.bitcoins.core.protocol.script.{
TaprootKeyPath TaprootKeyPath
} }
import org.bitcoins.core.protocol.transaction.TransactionOutput import org.bitcoins.core.protocol.transaction.TransactionOutput
import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
import org.bitcoins.core.script.constant.ScriptToken import org.bitcoins.core.script.constant.ScriptToken
import org.bitcoins.core.script.flag.{ScriptFlag, ScriptFlagUtil} import org.bitcoins.core.script.flag.{ScriptFlag, ScriptFlagUtil}
import org.bitcoins.core.script.result.{ import org.bitcoins.core.script.result.{
@ -35,33 +34,23 @@ trait TransactionSignatureChecker {
txSignatureComponent: TxSigComponent, txSignatureComponent: TxSigComponent,
pubKeyBytes: ECPublicKeyBytes, pubKeyBytes: ECPublicKeyBytes,
signature: ECDigitalSignature): TransactionSignatureCheckerResult = signature: ECDigitalSignature): TransactionSignatureCheckerResult =
checkSignature(txSignatureComponent, checkSignature(txSignatureComponent = txSignatureComponent,
PartialSignature(pubKeyBytes, signature)) script = txSignatureComponent.output.scriptPubKey.asm.toList,
pubKey = pubKeyBytes,
signature = signature)
def checkSignature( def checkSignature(
txSignatureComponent: TxSigComponent, txSignatureComponent: TxSigComponent,
pubKey: ECPublicKey, pubKey: ECPublicKey,
signature: ECDigitalSignature): TransactionSignatureCheckerResult = signature: ECDigitalSignature): TransactionSignatureCheckerResult = {
checkSignature(txSignatureComponent, PartialSignature(pubKey, signature)) checkSignature(
txSignatureComponent = txSignatureComponent,
def checkSignature( script = txSignatureComponent.output.scriptPubKey.asm.toList,
txSignatureComponent: TxSigComponent, pubKey = pubKey.toPublicKeyBytes(),
partialSignature: PartialSignature): TransactionSignatureCheckerResult = { signature = signature
checkSignature(txSignatureComponent, )
txSignatureComponent.output.scriptPubKey.asm.toList,
partialSignature.pubKey,
partialSignature.signature)
} }
def checkSignature(
txSignatureComponent: TxSigComponent,
script: Seq[ScriptToken],
partialSignature: PartialSignature): TransactionSignatureCheckerResult =
checkSignature(txSignatureComponent,
script,
partialSignature.pubKey,
partialSignature.signature)
/** @param txSigComponent /** @param txSigComponent
* @param schnorrSignature * @param schnorrSignature
* @param pubKey * @param pubKey

View File

@ -90,25 +90,6 @@ sealed abstract class TransactionSignatureCreator {
} }
} }
/** Creates a signature from a tx signature component
*
* @param privateKey
* the private key which we are signing the hash with
* @param hashType
* the procedure to use for hashing to transaction
* @return
*/
def createSig(
spendingTransaction: Transaction,
signingInfo: InputSigningInfo[InputInfo],
privateKey: ECPrivateKey,
hashType: HashType): ECDigitalSignature = {
createSig(spendingTransaction,
signingInfo,
privateKey.signWithHashType,
hashType)
}
/** This is intended to be a low level hardware wallet API. At a fundamental /** This is intended to be a low level hardware wallet API. At a fundamental
* level, a hardware wallet expects a scodec.bits.ByteVector as input, and * level, a hardware wallet expects a scodec.bits.ByteVector as input, and
* returns an [[ECDigitalSignature]] if it is able to sign the * returns an [[ECDigitalSignature]] if it is able to sign the
@ -122,11 +103,11 @@ sealed abstract class TransactionSignatureCreator {
* @return * @return
* the digital signature returned by the hardware wallet * the digital signature returned by the hardware wallet
*/ */
def createSig( def createSig[Sig <: DigitalSignature](
spendingTransaction: Transaction, spendingTransaction: Transaction,
signingInfo: InputSigningInfo[InputInfo], signingInfo: InputSigningInfo[InputInfo],
sign: (ByteVector, HashType) => ECDigitalSignature, sign: (ByteVector, HashType) => Sig,
hashType: HashType): ECDigitalSignature = { hashType: HashType): Sig = {
val hash = TransactionSignatureSerializer.hashForSignature( val hash = TransactionSignatureSerializer.hashForSignature(
spendingTransaction = spendingTransaction, spendingTransaction = spendingTransaction,
signingInfo = signingInfo, signingInfo = signingInfo,

View File

@ -3,12 +3,12 @@ package org.bitcoins.core.protocol.dlc.execution
import org.bitcoins.core.currency.CurrencyUnit import org.bitcoins.core.currency.CurrencyUnit
import org.bitcoins.core.protocol.dlc.build.DLCTxBuilder import org.bitcoins.core.protocol.dlc.build.DLCTxBuilder
import org.bitcoins.core.protocol.dlc.compute.{CETCalculator, DLCUtil} import org.bitcoins.core.protocol.dlc.compute.{CETCalculator, DLCUtil}
import org.bitcoins.core.protocol.dlc.models._ 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.core.util.Indexed
import org.bitcoins.crypto.{AdaptorSign, ECPublicKey} import org.bitcoins.crypto.{AdaptorSign, ECDigitalSignature, ECPublicKey}
import scala.util.{Success, Try} import scala.util.{Success, Try}
@ -22,7 +22,7 @@ case class DLCExecutor(signer: DLCTxSigner) {
*/ */
def setupDLCOffer( def setupDLCOffer(
cetSigs: CETSignatures, cetSigs: CETSignatures,
refundSig: PartialSignature): Try[SetupDLC] = { refundSig: PartialSignature[ECDigitalSignature]): Try[SetupDLC] = {
require(isInitiator, "You should call setupDLCAccept") require(isInitiator, "You should call setupDLCAccept")
setupDLC(cetSigs, refundSig, None, None) setupDLC(cetSigs, refundSig, None, None)
@ -34,7 +34,7 @@ case class DLCExecutor(signer: DLCTxSigner) {
*/ */
def setupDLCAccept( def setupDLCAccept(
cetSigs: CETSignatures, cetSigs: CETSignatures,
refundSig: PartialSignature, refundSig: PartialSignature[ECDigitalSignature],
fundingSigs: FundingSignatures, fundingSigs: FundingSignatures,
cetsOpt: Option[Vector[WitnessTransaction]]): Try[SetupDLC] = { cetsOpt: Option[Vector[WitnessTransaction]]): Try[SetupDLC] = {
require(!isInitiator, "You should call setupDLCOffer") require(!isInitiator, "You should call setupDLCOffer")
@ -47,7 +47,7 @@ case class DLCExecutor(signer: DLCTxSigner) {
*/ */
def setupDLC( def setupDLC(
cetSigs: CETSignatures, cetSigs: CETSignatures,
refundSig: PartialSignature, refundSig: PartialSignature[ECDigitalSignature],
fundingSigsOpt: Option[FundingSignatures], fundingSigsOpt: Option[FundingSignatures],
cetsOpt: Option[Vector[WitnessTransaction]]): Try[SetupDLC] = { cetsOpt: Option[Vector[WitnessTransaction]]): Try[SetupDLC] = {
if (!isInitiator) { if (!isInitiator) {
@ -114,7 +114,8 @@ case class DLCExecutor(signer: DLCTxSigner) {
RefundDLCOutcome(fundingTx, refundTx) RefundDLCOutcome(fundingTx, refundTx)
} }
def executeRefundDLC(refundSig: PartialSignature): RefundDLCOutcome = { def executeRefundDLC(
refundSig: PartialSignature[ECDigitalSignature]): RefundDLCOutcome = {
val refundTx = signer.completeRefundTx(refundSig) val refundTx = signer.completeRefundTx(refundSig)
val fundingTx = signer.builder.buildFundingTx val fundingTx = signer.builder.buildFundingTx
RefundDLCOutcome(fundingTx, refundTx) RefundDLCOutcome(fundingTx, refundTx)

View File

@ -196,7 +196,7 @@ object DLCMessage {
changeAddress: BitcoinAddress, changeAddress: BitcoinAddress,
payoutSerialId: UInt64, payoutSerialId: UInt64,
changeSerialId: UInt64, changeSerialId: UInt64,
refundSig: PartialSignature, refundSig: PartialSignature[ECDigitalSignature],
negotiationFields: DLCAccept.NegotiationFields, negotiationFields: DLCAccept.NegotiationFields,
tempContractId: Sha256Digest) { tempContractId: Sha256Digest) {
@ -229,7 +229,8 @@ object DLCMessage {
negotiationFields: DLCAccept.NegotiationFields, negotiationFields: DLCAccept.NegotiationFields,
tempContractId: Sha256Digest) { tempContractId: Sha256Digest) {
def withRefundSigs(refundSig: PartialSignature): DLCAcceptWithoutCetSigs = { def withRefundSigs(refundSig: PartialSignature[ECDigitalSignature])
: DLCAcceptWithoutCetSigs = {
DLCAcceptWithoutCetSigs( DLCAcceptWithoutCetSigs(
totalCollateral = totalCollateral, totalCollateral = totalCollateral,
pubKeys = pubKeys, pubKeys = pubKeys,
@ -245,7 +246,7 @@ object DLCMessage {
def withSigs( def withSigs(
cetSigs: CETSignatures, cetSigs: CETSignatures,
refundSig: PartialSignature): DLCAccept = { refundSig: PartialSignature[ECDigitalSignature]): DLCAccept = {
DLCAccept( DLCAccept(
collateral = totalCollateral, collateral = totalCollateral,
pubKeys = pubKeys, pubKeys = pubKeys,
@ -269,7 +270,7 @@ object DLCMessage {
payoutSerialId: UInt64, payoutSerialId: UInt64,
changeSerialId: UInt64, changeSerialId: UInt64,
cetSigs: CETSignatures, cetSigs: CETSignatures,
refundSig: PartialSignature, refundSig: PartialSignature[ECDigitalSignature],
negotiationFields: DLCAccept.NegotiationFields, negotiationFields: DLCAccept.NegotiationFields,
tempContractId: Sha256Digest, tempContractId: Sha256Digest,
isExternalAddress: Boolean = false) isExternalAddress: Boolean = false)
@ -428,7 +429,7 @@ object DLCMessage {
case class DLCSign( case class DLCSign(
cetSigs: CETSignatures, cetSigs: CETSignatures,
refundSig: PartialSignature, refundSig: PartialSignature[ECDigitalSignature],
fundingSigs: FundingSignatures, fundingSigs: FundingSignatures,
contractId: ByteVector) contractId: ByteVector)
extends DLCMessage { extends DLCMessage {

View File

@ -200,12 +200,13 @@ case class DLCTxSigner(
} }
/** Creates this party's signature of the refund transaction */ /** Creates this party's signature of the refund transaction */
lazy val signRefundTx: PartialSignature = { lazy val signRefundTx: PartialSignature[ECDigitalSignature] = {
DLCTxSigner.signRefundTx(cetSigningInfo, builder.buildRefundTx) DLCTxSigner.signRefundTx(cetSigningInfo, builder.buildRefundTx)
} }
/** Constructs the signed refund transaction given remote's signature */ /** Constructs the signed refund transaction given remote's signature */
def completeRefundTx(remoteSig: PartialSignature): WitnessTransaction = { def completeRefundTx(
remoteSig: PartialSignature[ECDigitalSignature]): WitnessTransaction = {
val localSig = signRefundTx val localSig = signRefundTx
DLCTxSigner.completeRefundTx(localSig, DLCTxSigner.completeRefundTx(localSig,
@ -414,7 +415,7 @@ object DLCTxSigner {
def signRefundTx( def signRefundTx(
refundSigningInfo: ECSignatureParams[P2WSHV0InputInfo], refundSigningInfo: ECSignatureParams[P2WSHV0InputInfo],
refundTx: WitnessTransaction refundTx: WitnessTransaction
): PartialSignature = { ): PartialSignature[ECDigitalSignature] = {
val fundingPubKey = refundSigningInfo.signer.publicKey val fundingPubKey = refundSigningInfo.signer.publicKey
val sig = TransactionSignatureCreator.createSig( val sig = TransactionSignatureCreator.createSig(
@ -428,8 +429,8 @@ object DLCTxSigner {
// TODO: Without PSBTs // TODO: Without PSBTs
def completeRefundTx( def completeRefundTx(
localSig: PartialSignature, localSig: PartialSignature[ECDigitalSignature],
remoteSig: PartialSignature, remoteSig: PartialSignature[ECDigitalSignature],
fundingMultiSig: MultiSignatureScriptPubKey, fundingMultiSig: MultiSignatureScriptPubKey,
fundingTx: Transaction, fundingTx: Transaction,
uRefundTx: WitnessTransaction): WitnessTransaction = { uRefundTx: WitnessTransaction): WitnessTransaction = {

View File

@ -17,7 +17,12 @@ 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.util.{FutureUtil, Indexed} import org.bitcoins.core.util.{FutureUtil, Indexed}
import org.bitcoins.crypto.{ECAdaptorSignature, ECPublicKey, HashType} import org.bitcoins.crypto.{
ECAdaptorSignature,
ECDigitalSignature,
ECPublicKey,
HashType
}
import scodec.bits.ByteVector import scodec.bits.ByteVector
import scala.concurrent.{ExecutionContext, Future} import scala.concurrent.{ExecutionContext, Future}
@ -76,7 +81,7 @@ case class DLCSignatureVerifier(builder: DLCTxBuilder, isInitiator: Boolean) {
} }
/** Verifies remote's refund signature */ /** Verifies remote's refund signature */
def verifyRefundSig(sig: PartialSignature): Boolean = { def verifyRefundSig(sig: PartialSignature[ECDigitalSignature]): Boolean = {
val refundTx = builder.buildRefundTx val refundTx = builder.buildRefundTx
DLCSignatureVerifier.validateRefundSignature(sig, DLCSignatureVerifier.validateRefundSignature(sig,
@ -114,7 +119,7 @@ object DLCSignatureVerifier {
} }
def validateRefundSignature( def validateRefundSignature(
refundSig: PartialSignature, refundSig: PartialSignature[ECDigitalSignature],
fundingTx: Transaction, fundingTx: Transaction,
fundOutputIndex: Int, fundOutputIndex: Int,
refundTx: WitnessTransaction refundTx: WitnessTransaction

View File

@ -2051,7 +2051,7 @@ case class DLCAcceptTLV(
negotiationFields.bytes negotiationFields.bytes
} }
val refundPartialSignature: PartialSignature = { val refundPartialSignature: PartialSignature[ECDigitalSignature] = {
PartialSignature(fundingPubKey, refundSignature) PartialSignature(fundingPubKey, refundSignature)
} }
} }
@ -2108,7 +2108,8 @@ case class DLCSignTLV(
fundingSignatures.bytes fundingSignatures.bytes
} }
def getPartialSignature(fundingPubKey: ECPublicKey): PartialSignature = { def getPartialSignature(
fundingPubKey: ECPublicKey): PartialSignature[ECDigitalSignature] = {
PartialSignature(fundingPubKey, refundSignature) PartialSignature(fundingPubKey, refundSignature)
} }
} }

View File

@ -690,18 +690,20 @@ case class PSBT(
PSBT(globalMap, newInputMaps, outputMaps) PSBT(globalMap, newInputMaps, outputMaps)
} }
def addSignature( def addSignature[Sig <: DigitalSignature](
pubKey: ECPublicKey, pubKey: ECPublicKey,
sig: ECDigitalSignature, sig: Sig,
inputIndex: Int): PSBT = inputIndex: Int): PSBT =
addSignature(PartialSignature(pubKey, sig), inputIndex) addSignature(PartialSignature(pubKey.toPublicKeyBytes(), sig), inputIndex)
def addSignature(partialSignature: PartialSignature, inputIndex: Int): PSBT = def addSignature[Sig <: DigitalSignature](
partialSignature: PartialSignature[Sig],
inputIndex: Int): PSBT =
addSignatures(Vector(partialSignature), inputIndex) addSignatures(Vector(partialSignature), inputIndex)
/** Adds all the PartialSignatures to the input map at the given index */ /** Adds all the PartialSignatures to the input map at the given index */
def addSignatures( def addSignatures[Sig <: DigitalSignature](
partialSignatures: Vector[PartialSignature], partialSignatures: Vector[PartialSignature[Sig]],
inputIndex: Int): PSBT = { inputIndex: Int): PSBT = {
require( require(
inputIndex < inputMaps.size, inputIndex < inputMaps.size,

View File

@ -1,6 +1,6 @@
package org.bitcoins.core.psbt package org.bitcoins.core.psbt
import org.bitcoins.crypto.Factory import org.bitcoins.crypto.{DigitalSignature, Factory}
import scodec.bits.ByteVector import scodec.bits.ByteVector
/** A PSBTKeyId refers to the first byte of a key that signifies which kind of /** A PSBTKeyId refers to the first byte of a key that signifies which kind of
@ -74,7 +74,7 @@ object PSBTInputKeyId extends PSBTKeyIdFactory[PSBTInputKeyId] {
byte match { byte match {
case NonWitnessUTXOKeyId.byte => NonWitnessUTXOKeyId case NonWitnessUTXOKeyId.byte => NonWitnessUTXOKeyId
case WitnessUTXOKeyId.byte => WitnessUTXOKeyId case WitnessUTXOKeyId.byte => WitnessUTXOKeyId
case PartialSignatureKeyId.byte => PartialSignatureKeyId case PartialSignatureKeyId.byte => PartialSignatureKeyId()
case SigHashTypeKeyId.byte => SigHashTypeKeyId case SigHashTypeKeyId.byte => SigHashTypeKeyId
case RedeemScriptKeyId.byte => RedeemScriptKeyId case RedeemScriptKeyId.byte => RedeemScriptKeyId
case WitnessScriptKeyId.byte => WitnessScriptKeyId case WitnessScriptKeyId.byte => WitnessScriptKeyId
@ -106,9 +106,14 @@ object PSBTInputKeyId extends PSBTKeyIdFactory[PSBTInputKeyId] {
type RecordType = InputPSBTRecord.WitnessUTXO type RecordType = InputPSBTRecord.WitnessUTXO
} }
case object PartialSignatureKeyId extends PSBTInputKeyId { case class PartialSignatureKeyId[Sig <: DigitalSignature]()
extends PSBTInputKeyId {
override val byte: Byte = 0x02.byteValue override val byte: Byte = 0x02.byteValue
type RecordType = InputPSBTRecord.PartialSignature type RecordType = InputPSBTRecord.PartialSignature[Sig]
}
object PartialSignatureKeyId {
val byte: Byte = PartialSignatureKeyId().byte
} }
case object SigHashTypeKeyId extends PSBTInputKeyId { case object SigHashTypeKeyId extends PSBTInputKeyId {

View File

@ -2,12 +2,12 @@ package org.bitcoins.core.psbt
import org.bitcoins.core.byteVectorOrdering import org.bitcoins.core.byteVectorOrdering
import org.bitcoins.core.number.UInt32 import org.bitcoins.core.number.UInt32
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.util.SeqWrapper import org.bitcoins.core.util.SeqWrapper
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.{HashType, _} import org.bitcoins.crypto.*
import scodec.bits.ByteVector import scodec.bits.ByteVector
import scala.annotation.tailrec import scala.annotation.tailrec
@ -234,7 +234,7 @@ case class InputPSBTMap(elements: Vector[InputPSBTRecord])
case multi: MultiSignatureScriptPubKey => case multi: MultiSignatureScriptPubKey =>
if (partialSignatures.size < multi.requiredSigs) { if (partialSignatures.size < multi.requiredSigs) {
val keys = multi.publicKeys.filterNot(key => val keys = multi.publicKeys.filterNot(key =>
partialSignatures.exists(_.pubKey == key)) partialSignatures[ECDigitalSignature].exists(_.pubKey == key))
keys.map(key => CryptoUtil.sha256Hash160(key.bytes)).toVector keys.map(key => CryptoUtil.sha256Hash160(key.bytes)).toVector
} else Vector.empty } else Vector.empty
case p2wpkh: P2WPKHWitnessSPKV0 => case p2wpkh: P2WPKHWitnessSPKV0 =>
@ -301,8 +301,9 @@ case class InputPSBTMap(elements: Vector[InputPSBTRecord])
getRecords(WitnessUTXOKeyId).headOption getRecords(WitnessUTXOKeyId).headOption
} }
def partialSignatures: Vector[PartialSignature] = { def partialSignatures[Sig <: DigitalSignature]
getRecords(PartialSignatureKeyId) : Vector[PartialSignature[Sig]] = {
getRecords(PartialSignatureKeyId[Sig]())
} }
def sigHashTypeOpt: Option[SigHashType] = { def sigHashTypeOpt: Option[SigHashType] = {
@ -440,17 +441,19 @@ case class InputPSBTMap(elements: Vector[InputPSBTRecord])
* @return * @return
* None if the requirement is not met * None if the requirement is not met
*/ */
def collectSigs( def collectSigs[Sig <: DigitalSignature](
required: Int, required: Int,
constructScriptSig: Seq[PartialSignature] => ScriptSignature) constructScriptSig: Seq[PartialSignature[Sig]] => ScriptSignature)
: Try[InputPSBTMap] = { : Try[InputPSBTMap] = {
val sigs = getRecords(PartialSignatureKeyId) val sigs = getRecords(PartialSignatureKeyId[Sig]())
if (sigs.length != required) { if (sigs.length != required) {
Failure(new IllegalArgumentException( Failure(new IllegalArgumentException(
s"Could not collect $required signatures when only the following were present: $sigs")) s"Could not collect $required signatures when only the following were present: $sigs"))
} else { } else {
val scriptSig = constructScriptSig( val scriptSig = constructScriptSig(sigs.map {
sigs.map(sig => PartialSignature(sig.pubKey, sig.signature))) case sig: PartialSignature[Sig] =>
PartialSignature(sig.pubKey, sig.signature)
})
val newInputMap = wipeAndAdd(scriptSig) val newInputMap = wipeAndAdd(scriptSig)
@ -504,15 +507,16 @@ case class InputPSBTMap(elements: Vector[InputPSBTRecord])
} }
case _: P2WPKHWitnessSPKV0 => case _: P2WPKHWitnessSPKV0 =>
val sigOpt = val sigOpt =
getRecords(PartialSignatureKeyId).headOption getRecords(PartialSignatureKeyId[ECDigitalSignature]()).headOption
toTry(sigOpt, "there is no partial signature record").map { sig => toTry(sigOpt, "there is no partial signature record").map {
val witness = P2WPKHWitnessV0(sig.pubKey, sig.signature) case sig: PartialSignature[ECDigitalSignature] =>
val scriptSig = EmptyScriptSignature val witness = P2WPKHWitnessV0(sig.pubKey, sig.signature)
wipeAndAdd(scriptSig, Some(witness)) val scriptSig = EmptyScriptSignature
wipeAndAdd(scriptSig, Some(witness))
} }
case p2pkWithTimeout: P2PKWithTimeoutScriptPubKey => case p2pkWithTimeout: P2PKWithTimeoutScriptPubKey =>
val sigOpt = val sigOpt =
getRecords(PartialSignatureKeyId).headOption getRecords(PartialSignatureKeyId[ECDigitalSignature]()).headOption
toTry(sigOpt, "there is no partial signature record").flatMap { sig => toTry(sigOpt, "there is no partial signature record").flatMap { sig =>
if (sig.pubKey == p2pkWithTimeout.pubKey) { if (sig.pubKey == p2pkWithTimeout.pubKey) {
val scriptSig = P2PKWithTimeoutScriptSignature(beforeTimeout = true, val scriptSig = P2PKWithTimeoutScriptSignature(beforeTimeout = true,
@ -591,7 +595,7 @@ case class InputPSBTMap(elements: Vector[InputPSBTRecord])
// Find the conditional leaf with the pubkeys for which sigs are provided // Find the conditional leaf with the pubkeys for which sigs are provided
// Hashes are used since we only have the pubkey hash in the p2pkh case // Hashes are used since we only have the pubkey hash in the p2pkh case
val sigs = getRecords(PartialSignatureKeyId) val sigs = getRecords(PartialSignatureKeyId[SchnorrDigitalSignature]())
val hashes = sigs.map(sig => CryptoUtil.sha256Hash160(sig.pubKey.bytes)) val hashes = sigs.map(sig => CryptoUtil.sha256Hash160(sig.pubKey.bytes))
addLeaves(conditional, Vector.empty) addLeaves(conditional, Vector.empty)
val leaves = builder.result() val leaves = builder.result()
@ -622,15 +626,16 @@ case class InputPSBTMap(elements: Vector[InputPSBTRecord])
case locktime: LockTimeScriptPubKey => case locktime: LockTimeScriptPubKey =>
finalize(locktime.nestedScriptPubKey) finalize(locktime.nestedScriptPubKey)
case _: P2PKHScriptPubKey => case _: P2PKHScriptPubKey =>
collectSigs( collectSigs[ECDigitalSignature](
required = 1, required = 1,
sigs => P2PKHScriptSignature(sigs.head.signature, sigs.head.pubKey)) sigs => P2PKHScriptSignature(sigs.head.signature, sigs.head.pubKey))
case _: P2PKScriptPubKey => case _: P2PKScriptPubKey =>
collectSigs(required = 1, collectSigs[ECDigitalSignature](
sigs => P2PKScriptSignature(sigs.head.signature)) required = 1,
sigs => P2PKScriptSignature(sigs.head.signature))
case multiSig: MultiSignatureScriptPubKey => case multiSig: MultiSignatureScriptPubKey =>
def generateScriptSig( def generateScriptSig(sigs: Seq[PartialSignature[ECDigitalSignature]])
sigs: Seq[PartialSignature]): MultiSignatureScriptSignature = { : MultiSignatureScriptSignature = {
val sortedSigs = sigs val sortedSigs = sigs
.map { partialSig => .map { partialSig =>
(partialSig.signature, (partialSig.signature,
@ -941,7 +946,26 @@ object InputPSBTMap extends PSBTMapFactory[InputPSBTRecord, InputPSBTMap] {
spendingInfo: ScriptSignatureParams[InputInfo], spendingInfo: ScriptSignatureParams[InputInfo],
unsignedTx: Transaction): InputPSBTMap = { unsignedTx: Transaction): InputPSBTMap = {
val sigs = spendingInfo.toSingles.map { spendingInfoSingle => val sigs = spendingInfo.toSingles.map { spendingInfoSingle =>
BitcoinSigner.signSingle(spendingInfoSingle, unsignedTx) spendingInfoSingle.inputInfo.scriptPubKey match {
case _: NonWitnessScriptPubKey =>
BitcoinSigner.signSingle(
spendingInfoSingle,
unsignedTx,
spendingInfoSingle.signer.signLowRWithHashType)
case _: WitnessScriptPubKeyV0 =>
BitcoinSigner.signSingle(
spendingInfoSingle,
unsignedTx,
spendingInfoSingle.signer.signLowRWithHashType)
case _: TaprootScriptPubKey =>
BitcoinSigner.signSingle(
spendingInfoSingle,
unsignedTx,
spendingInfoSingle.signer.schnorrSignWithHashType)
case u: UnassignedWitnessScriptPubKey =>
sys.error(s"Cannot sign unsupported witSPK=$u")
}
} }
val builder = Vector.newBuilder[InputPSBTRecord] val builder = Vector.newBuilder[InputPSBTRecord]

View File

@ -157,47 +157,53 @@ object InputPSBTRecord extends Factory[InputPSBTRecord] {
override val value: ByteVector = witnessUTXO.bytes override val value: ByteVector = witnessUTXO.bytes
} }
case class PartialSignature( case class PartialSignature[Sig <: DigitalSignature](
pubKey: ECPublicKeyBytes, pubKey: ECPublicKeyBytes,
signature: ECDigitalSignature) signature: Sig)
extends InputPSBTRecord { extends InputPSBTRecord {
require(pubKey.byteSize == 33, require(pubKey.byteSize == 33,
s"pubKey must be 33 bytes, got: ${pubKey.byteSize}") s"pubKey must be 33 bytes, got: ${pubKey.byteSize}")
override type KeyId = PartialSignatureKeyId.type override type KeyId = PartialSignatureKeyId[DigitalSignature]
override val key: ByteVector = override val key: ByteVector =
ByteVector(PartialSignatureKeyId.byte) ++ pubKey.bytes ByteVector(PartialSignatureKeyId.byte) ++ pubKey.bytes
override val value: ByteVector = signature.bytes override val value: ByteVector = signature.bytes
} }
object PartialSignature extends Factory[PartialSignature] { object PartialSignature extends Factory[PartialSignature[DigitalSignature]] {
def apply( def apply(
pubKey: ECPublicKey, pubKey: ECPublicKey,
signature: ECDigitalSignature): PartialSignature = { signature: ECDigitalSignature): PartialSignature[ECDigitalSignature] = {
PartialSignature(pubKey.toPublicKeyBytes(), signature) PartialSignature(pubKey.toPublicKeyBytes(), signature)
} }
def dummyPartialSig( def apply(pubKey: ECPublicKey, signature: SchnorrDigitalSignature)
pubKey: ECPublicKey = ECPublicKey.freshPublicKey): PartialSignature = { : PartialSignature[SchnorrDigitalSignature] = {
PartialSignature(pubKey.toPublicKeyBytes(), signature)
}
def dummyPartialSig(pubKey: ECPublicKey = ECPublicKey.freshPublicKey)
: PartialSignature[ECDigitalSignature] = {
PartialSignature(pubKey, ECDigitalSignature.dummy) PartialSignature(pubKey, ECDigitalSignature.dummy)
} }
override def fromBytes(bytes: ByteVector): PartialSignature = override def fromBytes(
bytes: ByteVector): PartialSignature[DigitalSignature] =
InputPSBTRecord(bytes) match { InputPSBTRecord(bytes) match {
case partialSignature: PartialSignature => case partialSignature: PartialSignature[DigitalSignature] @unchecked =>
partialSignature partialSignature
case other: InputPSBTRecord => case other: InputPSBTRecord =>
throw new IllegalArgumentException( throw new IllegalArgumentException(
s"Invalid PartialSignature encoding, got: $other") s"Invalid PartialSignature encoding, got: $other")
} }
def vecFromBytes(bytes: ByteVector): Vector[PartialSignature] = { def vecFromBytes(bytes: ByteVector): Vector[PartialSignature[?]] = {
@scala.annotation.tailrec @scala.annotation.tailrec
def loop( def loop(
remainingBytes: ByteVector, remainingBytes: ByteVector,
accum: Vector[PartialSignature]): Vector[PartialSignature] = { accum: Vector[PartialSignature[?]]): Vector[PartialSignature[?]] = {
if (remainingBytes.isEmpty) { if (remainingBytes.isEmpty) {
accum accum
} else { } else {
@ -211,7 +217,7 @@ object InputPSBTRecord extends Factory[InputPSBTRecord] {
loop(bytes, Vector.empty) loop(bytes, Vector.empty)
} }
def vecFromHex(hex: String): Vector[PartialSignature] = { def vecFromHex(hex: String): Vector[PartialSignature[?]] = {
vecFromBytes(BytesUtil.decodeHex(hex)) vecFromBytes(BytesUtil.decodeHex(hex))
} }
} }
@ -419,10 +425,15 @@ object InputPSBTRecord extends Factory[InputPSBTRecord] {
s"The key must only contain the 1 byte type, got: ${key.size}") s"The key must only contain the 1 byte type, got: ${key.size}")
WitnessUTXO(TransactionOutput.fromBytes(value)) WitnessUTXO(TransactionOutput.fromBytes(value))
case PartialSignatureKeyId => case PartialSignatureKeyId() =>
val pubKey = ECPublicKey(key.tail) val pubKey = ECPublicKey(key.tail)
val sig = ECDigitalSignature(value) if (value.length == 64 || value.length == 65) {
PartialSignature(pubKey, sig) val sig = SchnorrDigitalSignature(value)
PartialSignature(pubKey.toPublicKeyBytes(), sig)
} else {
val sig = ECDigitalSignature(value)
PartialSignature(pubKey.toPublicKeyBytes(), sig)
}
case SigHashTypeKeyId => case SigHashTypeKeyId =>
require(key.size == 1, require(key.size == 1,
s"The key must only contain the 1 byte type, got: ${key.size}") s"The key must only contain the 1 byte type, got: ${key.size}")

View File

@ -15,20 +15,22 @@ import scodec.bits.ByteVector
sealed abstract class SignerUtils { sealed abstract class SignerUtils {
def doSign( def doSign[Sig <: DigitalSignature](
unsignedTx: Transaction, unsignedTx: Transaction,
signingInfo: InputSigningInfo[InputInfo], signingInfo: InputSigningInfo[InputInfo],
sign: (ByteVector, HashType) => ECDigitalSignature, sign: (ByteVector, HashType) => Sig,
hashType: HashType): ECDigitalSignature = { hashType: HashType): Sig = {
TransactionSignatureCreator.createSig(unsignedTx, TransactionSignatureCreator.createSig(unsignedTx,
signingInfo, signingInfo,
sign, sign,
hashType) hashType)
} }
def signSingle( def signSingle[Sig <: DigitalSignature](
spendingInfo: ECSignatureParams[InputInfo], spendingInfo: ECSignatureParams[InputInfo],
unsignedTx: Transaction): PartialSignature = { unsignedTx: Transaction,
signWithHashType: (ByteVector, HashType) => Sig)
: PartialSignature[Sig] = {
val tx = spendingInfo.inputInfo match { val tx = spendingInfo.inputInfo match {
case _: SegwitV0NativeInputInfo | _: P2SHNestedSegwitV0InputInfo | case _: SegwitV0NativeInputInfo | _: P2SHNestedSegwitV0InputInfo |
@ -38,14 +40,15 @@ sealed abstract class SignerUtils {
unsignedTx unsignedTx
} }
val signature = doSign( val signature: Sig = doSign(
unsignedTx = tx, unsignedTx = tx,
signingInfo = spendingInfo, signingInfo = spendingInfo,
sign = spendingInfo.signer.signLowRWithHashType, sign = signWithHashType,
hashType = spendingInfo.hashType hashType = spendingInfo.hashType
) )
PartialSignature(spendingInfo.signer.publicKey, signature) PartialSignature(spendingInfo.signer.publicKey.toPublicKeyBytes(),
signature)
} }
protected val flags: Seq[ScriptFlag] = Policy.standardFlags protected val flags: Seq[ScriptFlag] = Policy.standardFlags
@ -206,12 +209,11 @@ object BitcoinSigner extends SignerUtils {
signer: Sign, signer: Sign,
conditionalPath: ConditionalPath = ConditionalPath.NoCondition): PSBT = { conditionalPath: ConditionalPath = ConditionalPath.NoCondition): PSBT = {
// if already signed by this signer // if already signed by this signer
if ( val partialSigs = psbt
psbt .inputMaps(inputIndex)
.inputMaps(inputIndex) .partialSignatures
.partialSignatures val isSigned = partialSigs.exists(_.pubKey.toPublicKey == signer.publicKey)
.exists(_.pubKey.toPublicKey == signer.publicKey) if (isSigned) {
) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Input has already been signed with this key") "Input has already been signed with this key")
} }
@ -222,9 +224,9 @@ object BitcoinSigner extends SignerUtils {
.inputMaps(inputIndex) .inputMaps(inputIndex)
.toUTXOSigningInfo(tx.inputs(inputIndex), signer, conditionalPath) .toUTXOSigningInfo(tx.inputs(inputIndex), signer, conditionalPath)
val txToSign = spendingInfo.output.scriptPubKey match { val partialSignature = spendingInfo.output.scriptPubKey match {
case _: WitnessScriptPubKey => case _: WitnessScriptPubKeyV0 =>
tx match { val txToSign = tx match {
case btx: NonWitnessTransaction => case btx: NonWitnessTransaction =>
val witnesses = psbt.inputMaps.map { map => val witnesses = psbt.inputMaps.map { map =>
map.witnessScriptOpt.map(scriptWit => map.witnessScriptOpt.map(scriptWit =>
@ -253,12 +255,15 @@ object BitcoinSigner extends SignerUtils {
wtx wtx
} }
} }
case _: ScriptPubKey => tx signSingle(spendingInfo, txToSign, signer.signLowRWithHashType)
case _: TaprootScriptPubKey =>
signSingle(spendingInfo, tx, signer.schnorrSignWithHashType)
case _: NonWitnessScriptPubKey =>
signSingle(spendingInfo, tx, signer.signLowRWithHashType)
case u: UnassignedWitnessScriptPubKey =>
sys.error(s"Cannot sign unsupported witSPK=$u")
} }
val partialSignature =
signSingle(spendingInfo, txToSign)
psbt.addSignature(partialSignature, inputIndex) psbt.addSignature(partialSignature, inputIndex)
} }
} }
@ -280,8 +285,9 @@ sealed abstract class RawSingleKeyBitcoinSigner[-InputType <: RawInputInfo]
val (_, output, inputIndex, _) = val (_, output, inputIndex, _) =
relevantInfo(spendingInfo, unsignedTx) relevantInfo(spendingInfo, unsignedTx)
val single = spendingInfo.toSingle(0)
val partialSignature = val partialSignature =
signSingle(spendingInfo.toSingle(0), unsignedTx) signSingle(single, unsignedTx, single.signer.signLowRWithHashType)
val scriptSig = val scriptSig =
keyAndSigToScriptSig(partialSignature.pubKey.toPublicKey, keyAndSigToScriptSig(partialSignature.pubKey.toPublicKey,
@ -371,7 +377,9 @@ sealed abstract class MultiSigSigner extends Signer[MultiSignatureInputInfo] {
relevantInfo(spendingInfo, unsignedTx) relevantInfo(spendingInfo, unsignedTx)
val keysAndSigs = spendingInfo.toSingles.map { spendingInfoSingle => val keysAndSigs = spendingInfo.toSingles.map { spendingInfoSingle =>
signSingle(spendingInfoSingle, unsignedTx) signSingle(spendingInfoSingle,
unsignedTx,
spendingInfoSingle.signer.signLowRWithHashType)
} }
val signatures = keysAndSigs.map(_.signature) val signatures = keysAndSigs.map(_.signature)

View File

@ -68,7 +68,7 @@ case class ScriptSignatureParams[+InputType <: InputInfo](
def signer: Sign = { def signer: Sign = {
require( require(
signers.length == 1, signers.length == 1,
"This method is for spending infos with a single signer, if you mean signers.head be explicit") s"This method is for spending infos with a single signer, if you mean signers.head be explicit, signers=$signers")
signers.head signers.head
} }

View File

@ -423,18 +423,32 @@ class DbCommonsColumnMappers(val profile: JdbcProfile) {
) )
} }
implicit val partialSigMapper: BaseColumnType[PartialSignature] = { implicit val partialSigMapper
: BaseColumnType[PartialSignature[DigitalSignature]] = {
MappedColumnType MappedColumnType
.base[PartialSignature, String](_.hex, PartialSignature.fromHex) .base[PartialSignature[DigitalSignature], String](
_.hex,
PartialSignature.fromHex)
} }
implicit val partialSigsMapper: BaseColumnType[Vector[PartialSignature]] = { implicit val ecPartialSigMapper
: BaseColumnType[PartialSignature[ECDigitalSignature]] = {
MappedColumnType MappedColumnType
.base[Vector[PartialSignature], String]( .base[PartialSignature[ECDigitalSignature], String](
_.hex,
PartialSignature
.fromHex(_)
.asInstanceOf[PartialSignature[ECDigitalSignature]])
}
implicit val partialSigsMapper
: BaseColumnType[Vector[PartialSignature[DigitalSignature]]] = {
MappedColumnType
.base[Vector[PartialSignature[DigitalSignature]], String](
_.foldLeft("")(_ ++ _.hex), _.foldLeft("")(_ ++ _.hex),
hex => hex =>
if (hex.isEmpty) Vector.empty if (hex.isEmpty) Vector.empty
else InputPSBTMap(hex ++ "00").partialSignatures else InputPSBTMap(hex ++ "00").partialSignatures[DigitalSignature]
) )
} }

View File

@ -31,6 +31,7 @@ import org.bitcoins.core.wallet.utxo.{
} }
import org.bitcoins.crypto.{ import org.bitcoins.crypto.{
DoubleSha256DigestBE, DoubleSha256DigestBE,
ECDigitalSignature,
SchnorrDigitalSignature, SchnorrDigitalSignature,
Sha256Digest Sha256Digest
} }
@ -396,7 +397,7 @@ case class DLCTransactionProcessing(
private def buildSignMessage( private def buildSignMessage(
dlcDb: DLCDb, dlcDb: DLCDb,
sigDbs: Vector[DLCCETSignaturesDb], sigDbs: Vector[DLCCETSignaturesDb],
offerRefundSig: PartialSignature, offerRefundSig: PartialSignature[ECDigitalSignature],
fundingInputDbs: Vector[DLCFundingInputDb] fundingInputDbs: Vector[DLCFundingInputDb]
): DLCSign = { ): DLCSign = {
{ {

View File

@ -28,7 +28,7 @@ case class DLCAcceptDb(
tempContractId: Sha256Digest, tempContractId: Sha256Digest,
fundingInputs: Vector[DLCFundingInput], fundingInputs: Vector[DLCFundingInput],
outcomeSigs: Vector[(ECPublicKey, ECAdaptorSignature)], outcomeSigs: Vector[(ECPublicKey, ECAdaptorSignature)],
refundSig: PartialSignature refundSig: PartialSignature[ECDigitalSignature]
): DLCAccept = { ): DLCAccept = {
val pubKeys = val pubKeys =
DLCPublicKeys(fundingKey, payoutAddress) DLCPublicKeys(fundingKey, payoutAddress)

View File

@ -2,7 +2,7 @@ package org.bitcoins.dlc.wallet.models
import org.bitcoins.core.api.dlc.wallet.db.DLCDb import org.bitcoins.core.api.dlc.wallet.db.DLCDb
import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
import org.bitcoins.crypto.Sha256Digest import org.bitcoins.crypto.{ECDigitalSignature, Sha256Digest}
import org.bitcoins.db.{CRUD, SlickUtil} import org.bitcoins.db.{CRUD, SlickUtil}
import org.bitcoins.dlc.wallet.DLCAppConfig import org.bitcoins.dlc.wallet.DLCAppConfig
import slick.lifted.{ForeignKeyQuery, ProvenShape} import slick.lifted.{ForeignKeyQuery, ProvenShape}
@ -71,9 +71,11 @@ case class DLCRefundSigsDAO()(implicit
def dlcId: Rep[Sha256Digest] = column("dlc_id", O.PrimaryKey) def dlcId: Rep[Sha256Digest] = column("dlc_id", O.PrimaryKey)
def accepterSig: Rep[PartialSignature] = column("accepter_sig") def accepterSig: Rep[PartialSignature[ECDigitalSignature]] = column(
"accepter_sig")
def initiatorSig: Rep[Option[PartialSignature]] = column("initiator_sig") def initiatorSig: Rep[Option[PartialSignature[ECDigitalSignature]]] =
column("initiator_sig")
def * : ProvenShape[DLCRefundSigsDb] = def * : ProvenShape[DLCRefundSigsDb] =
(dlcId, accepterSig, initiatorSig).<>( (dlcId, accepterSig, initiatorSig).<>(

View File

@ -1,10 +1,10 @@
package org.bitcoins.dlc.wallet.models package org.bitcoins.dlc.wallet.models
import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
import org.bitcoins.crypto.Sha256Digest import org.bitcoins.crypto.{ECDigitalSignature, Sha256Digest}
case class DLCRefundSigsDb( case class DLCRefundSigsDb(
dlcId: Sha256Digest, dlcId: Sha256Digest,
accepterSig: PartialSignature, accepterSig: PartialSignature[ECDigitalSignature],
initiatorSig: Option[PartialSignature] initiatorSig: Option[PartialSignature[ECDigitalSignature]]
) )

View File

@ -126,7 +126,7 @@ sealed abstract class Signer[-InputType <: InputInfo] {
def signSingle( def signSingle(
spendingInfo: UTXOSigningInfo[InputInfo], spendingInfo: UTXOSigningInfo[InputInfo],
unsignedTx: Transaction)( unsignedTx: Transaction)(
implicit ec: ExecutionContext): Future[PartialSignature] = ??? implicit ec: ExecutionContext): Future[PartialSignature[ECDigitalSignature]] = ???
} }
sealed abstract class RawSingleKeyBitcoinSigner[-InputType <: RawInputInfo] sealed abstract class RawSingleKeyBitcoinSigner[-InputType <: RawInputInfo]
extends Signer[InputType] { extends Signer[InputType] {
@ -180,7 +180,7 @@ object BitcoinSigner {
def signSingle( def signSingle(
spendingInfo: UTXOSigningInfo[InputInfo], spendingInfo: UTXOSigningInfo[InputInfo],
unsignedTx: Transaction)( unsignedTx: Transaction)(
implicit ec: ExecutionContext): Future[PartialSignature] = ??? implicit ec: ExecutionContext): Future[PartialSignature[ECDigitalSignature]] = ???
} }
def asm: Seq[ScriptToken] = ??? def asm: Seq[ScriptToken] = ???

View File

@ -127,7 +127,8 @@ val spendingInfoSingle = ECSignatureParams(
// Then we can sign the transaction // Then we can sign the transaction
val signature = BitcoinSigner.signSingle( val signature = BitcoinSigner.signSingle(
spendingInfo = spendingInfoSingle, spendingInfo = spendingInfoSingle,
unsignedTx = unsignedTransaction) unsignedTx = unsignedTransaction,
privKey0.signLowRWithHashType)
// We can then add the signature to the PSBT // We can then add the signature to the PSBT
// Note: this signature could be produced by us or another party // Note: this signature could be produced by us or another party

View File

@ -699,22 +699,27 @@ trait DLCTest {
publishTransaction: Transaction => Future[?] publishTransaction: Transaction => Future[?]
)(implicit ec: ExecutionContext): Future[(SetupDLC, SetupDLC)] = { )(implicit ec: ExecutionContext): Future[(SetupDLC, SetupDLC)] = {
val offerSigReceiveP = { val offerSigReceiveP = {
Promise[(CETSignatures, PartialSignature)]() Promise[(CETSignatures, PartialSignature[ECDigitalSignature])]()
} }
val sendAcceptSigs: (CETSignatures, PartialSignature) => Future[Unit] = { val sendAcceptSigs: (
case (cetSigs: CETSignatures, refundSig: PartialSignature) => CETSignatures,
PartialSignature[ECDigitalSignature]) => Future[Unit] = {
case (cetSigs: CETSignatures,
refundSig: PartialSignature[ECDigitalSignature]) =>
val _ = offerSigReceiveP.success((cetSigs, refundSig)) val _ = offerSigReceiveP.success((cetSigs, refundSig))
FutureUtil.unit FutureUtil.unit
} }
val acceptSigReceiveP = { val acceptSigReceiveP = {
Promise[(CETSignatures, PartialSignature, FundingSignatures)]() Promise[(CETSignatures,
PartialSignature[ECDigitalSignature],
FundingSignatures)]()
} }
val sendOfferSigs = { val sendOfferSigs = {
( (
cetSigs: CETSignatures, cetSigs: CETSignatures,
refundSig: PartialSignature, refundSig: PartialSignature[ECDigitalSignature],
fundingSigs: FundingSignatures fundingSigs: FundingSignatures
) => ) =>
val _ = acceptSigReceiveP.success((cetSigs, refundSig, fundingSigs)) val _ = acceptSigReceiveP.success((cetSigs, refundSig, fundingSigs))

View File

@ -74,8 +74,12 @@ case class TestDLCClient(
* from them * from them
*/ */
def setupDLCAccept( def setupDLCAccept(
sendSigs: (CETSignatures, PartialSignature) => Future[Unit], sendSigs: (
getSigs: Future[(CETSignatures, PartialSignature, FundingSignatures)] CETSignatures,
PartialSignature[ECDigitalSignature]) => Future[Unit],
getSigs: Future[(CETSignatures,
PartialSignature[ECDigitalSignature],
FundingSignatures)]
): Future[SetupDLC] = { ): Future[SetupDLC] = {
require(!isInitiator, "You should call setupDLCOffer") require(!isInitiator, "You should call setupDLCOffer")
@ -98,10 +102,10 @@ case class TestDLCClient(
* signed funding transaction * signed funding transaction
*/ */
def setupDLCOffer( def setupDLCOffer(
getSigs: Future[(CETSignatures, PartialSignature)], getSigs: Future[(CETSignatures, PartialSignature[ECDigitalSignature])],
sendSigs: ( sendSigs: (
CETSignatures, CETSignatures,
PartialSignature, PartialSignature[ECDigitalSignature],
FundingSignatures FundingSignatures
) => Future[Unit], ) => Future[Unit],
getFundingTx: Future[Transaction] getFundingTx: Future[Transaction]

View File

@ -148,7 +148,7 @@ object PSBTGenerators {
val newInputsMaps = psbt.inputMaps.map { map => val newInputsMaps = psbt.inputMaps.map { map =>
InputPSBTMap( InputPSBTMap(
map.elements.filterNot(element => map.elements.filterNot(element =>
PSBTInputKeyId.fromBytes(element.key) == PartialSignatureKeyId) PSBTInputKeyId.fromBytes(element.key) == PartialSignatureKeyId())
) )
} }

View File

@ -23,7 +23,8 @@ object BytesUtil {
ECDigitalSignature(flipAtIndex(signature.bytes, 60)) ECDigitalSignature(flipAtIndex(signature.bytes, 60))
} }
def flipBit(partialSignature: PartialSignature): PartialSignature = { def flipBit(partialSignature: PartialSignature[ECDigitalSignature])
: PartialSignature[ECDigitalSignature] = {
partialSignature.copy(signature = flipBit(partialSignature.signature)) partialSignature.copy(signature = flipBit(partialSignature.signature))
} }
@ -63,8 +64,8 @@ object BytesUtil {
def flipBit( def flipBit(
cetSigs: CETSignatures, cetSigs: CETSignatures,
refundSig: PartialSignature refundSig: PartialSignature[ECDigitalSignature]
): (CETSignatures, PartialSignature) = { ): (CETSignatures, PartialSignature[ECDigitalSignature]) = {
val badOutcomeSigs = cetSigs.outcomeSigs.map { case (outcome, sig) => val badOutcomeSigs = cetSigs.outcomeSigs.map { case (outcome, sig) =>
outcome -> flipBit(sig) outcome -> flipBit(sig)
} }

View File

@ -8,7 +8,12 @@ import org.bitcoins.core.protocol.script.{
ScriptWitnessV0 ScriptWitnessV0
} }
import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
import org.bitcoins.crypto.{ECAdaptorSignature, ECDigitalSignature} import org.bitcoins.crypto.{
DigitalSignature,
ECAdaptorSignature,
ECDigitalSignature,
SchnorrDigitalSignature
}
import scodec.bits.ByteVector import scodec.bits.ByteVector
object BytesUtil { object BytesUtil {
@ -23,8 +28,19 @@ object BytesUtil {
ECDigitalSignature(flipAtIndex(signature.bytes, 60)) ECDigitalSignature(flipAtIndex(signature.bytes, 60))
} }
def flipBit(partialSignature: PartialSignature): PartialSignature = { def flipBit(signature: SchnorrDigitalSignature): SchnorrDigitalSignature = {
partialSignature.copy(signature = flipBit(partialSignature.signature)) SchnorrDigitalSignature(flipAtIndex(signature.bytes, 60))
}
def flipBit[Sig <: DigitalSignature](
partialSignature: PartialSignature[Sig]): PartialSignature[Sig] = {
val s = partialSignature.signature match {
case e: ECDigitalSignature => flipBit(e).asInstanceOf[Sig]
case s: SchnorrDigitalSignature => flipBit(s).asInstanceOf[Sig]
case d: DigitalSignature =>
sys.error(s"Cannot flip bit on unknown digital signature type=$d")
}
partialSignature.copy(signature = s)
} }
def flipBit(adaptorSignature: ECAdaptorSignature): ECAdaptorSignature = { def flipBit(adaptorSignature: ECAdaptorSignature): ECAdaptorSignature = {
@ -63,8 +79,8 @@ object BytesUtil {
def flipBit( def flipBit(
cetSigs: CETSignatures, cetSigs: CETSignatures,
refundSig: PartialSignature refundSig: PartialSignature[ECDigitalSignature]
): (CETSignatures, PartialSignature) = { ): (CETSignatures, PartialSignature[ECDigitalSignature]) = {
val badOutcomeSigs = cetSigs.outcomeSigs.map { case (outcome, sig) => val badOutcomeSigs = cetSigs.outcomeSigs.map { case (outcome, sig) =>
outcome -> flipBit(sig) outcome -> flipBit(sig)
} }

View File

@ -182,10 +182,10 @@ object DLCWalletUtil extends BitcoinSLogger {
lazy val dummyKey2: ECPublicKey = ECPublicKey.freshPublicKey lazy val dummyKey2: ECPublicKey = ECPublicKey.freshPublicKey
lazy val dummyPartialSig: PartialSignature = lazy val dummyPartialSig: PartialSignature[ECDigitalSignature] =
PartialSignature(dummyKey, ECDigitalSignature.dummy) PartialSignature(dummyKey, ECDigitalSignature.dummy)
lazy val minimalPartialSig: PartialSignature = { lazy val minimalPartialSig: PartialSignature[ECDigitalSignature] = {
PartialSignature(dummyKey, ECDigitalSignature.minimalEncodedZeroSig) PartialSignature(dummyKey, ECDigitalSignature.minimalEncodedZeroSig)
} }

View File

@ -6,13 +6,13 @@ import org.bitcoins.core.api.wallet.db.{AddressDb, TransactionDbHelper}
import org.bitcoins.core.hd.HDChainType.{Change, External} import org.bitcoins.core.hd.HDChainType.{Change, External}
import org.bitcoins.core.hd.{AddressType, HDAccount, HDChainType} import org.bitcoins.core.hd.{AddressType, HDAccount, HDChainType}
import org.bitcoins.core.protocol.BitcoinAddress import org.bitcoins.core.protocol.BitcoinAddress
import org.bitcoins.core.protocol.script._ import org.bitcoins.core.protocol.script.*
import org.bitcoins.core.util.FutureUtil import org.bitcoins.core.util.FutureUtil
import org.bitcoins.crypto.{CryptoUtil, ECPublicKey} import org.bitcoins.crypto.{CryptoUtil, ECDigitalSignature, ECPublicKey}
import org.bitcoins.keymanager.{DecryptedMnemonic, WalletStorage} import org.bitcoins.keymanager.{DecryptedMnemonic, WalletStorage}
import org.bitcoins.testkit.chain.MockChainQueryApi import org.bitcoins.testkit.chain.MockChainQueryApi
import org.bitcoins.testkit.wallet.BitcoinSWalletTest import org.bitcoins.testkit.wallet.BitcoinSWalletTest
import org.bitcoins.testkitcore.util.TransactionTestUtil._ import org.bitcoins.testkitcore.util.TransactionTestUtil.*
import org.scalatest.FutureOutcome import org.scalatest.FutureOutcome
import org.scalatest.compatible.Assertion import org.scalatest.compatible.Assertion
@ -238,7 +238,8 @@ class WalletUnitTest extends BitcoinSWalletTest {
} yield { } yield {
assert(signed != psbt) assert(signed != psbt)
assert( assert(
signed.inputMaps.head.partialSignatures signed.inputMaps.head
.partialSignatures[ECDigitalSignature]
.exists(_.pubKey.toPublicKey == walletKey) .exists(_.pubKey.toPublicKey == walletKey)
) )
} }
@ -264,7 +265,8 @@ class WalletUnitTest extends BitcoinSWalletTest {
} yield { } yield {
assert(signed != psbt) assert(signed != psbt)
assert( assert(
signed.inputMaps.head.partialSignatures signed.inputMaps.head
.partialSignatures[ECDigitalSignature]
.exists(_.pubKey.toPublicKey == walletKey) .exists(_.pubKey.toPublicKey == walletKey)
) )
} }
@ -290,7 +292,8 @@ class WalletUnitTest extends BitcoinSWalletTest {
} yield { } yield {
assert(signed != psbt) assert(signed != psbt)
assert( assert(
signed.inputMaps.head.partialSignatures signed.inputMaps.head
.partialSignatures[ECDigitalSignature]
.exists(_.pubKey.toPublicKey == walletKey) .exists(_.pubKey.toPublicKey == walletKey)
) )
} }
@ -317,7 +320,8 @@ class WalletUnitTest extends BitcoinSWalletTest {
} yield { } yield {
assert(signed != psbt) assert(signed != psbt)
assert( assert(
signed.inputMaps.head.partialSignatures signed.inputMaps.head
.partialSignatures[ECDigitalSignature]
.exists(_.pubKey.toPublicKey == walletKey) .exists(_.pubKey.toPublicKey == walletKey)
) )
} }

View File

@ -47,7 +47,7 @@ import org.bitcoins.core.wallet.fee.{
SatoshisPerVirtualByte SatoshisPerVirtualByte
} }
import org.bitcoins.core.wallet.utxo.{AddressTag, TxoState} import org.bitcoins.core.wallet.utxo.{AddressTag, TxoState}
import org.bitcoins.crypto.{CryptoUtil, DoubleSha256DigestBE} import org.bitcoins.crypto.{CryptoUtil, DigitalSignature, DoubleSha256DigestBE}
import org.bitcoins.wallet.config.WalletAppConfig import org.bitcoins.wallet.config.WalletAppConfig
import org.bitcoins.wallet.models.{ import org.bitcoins.wallet.models.{
AccountDAO, AccountDAO,
@ -560,11 +560,12 @@ case class SendFundsHandlingHandling(
keyPaths.foldLeft(withData) { (accum, hdPath) => keyPaths.foldLeft(withData) { (accum, hdPath) =>
val sign = keyManager.toSign(hdPath) val sign = keyManager.toSign(hdPath)
// Only sign if that key doesn't have a signature yet // Only sign if that key doesn't have a signature yet
if ( val sigExists = input
!input.partialSignatures.exists( .partialSignatures[DigitalSignature]
.exists(
_.pubKey.toPublicKey == sign.publicKey _.pubKey.toPublicKey == sign.publicKey
) )
) { if (!sigExists) {
logger.debug( logger.debug(
s"Signing input $index with key ${sign.publicKey.hex}" s"Signing input $index with key ${sign.publicKey.hex}"
) )