mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-19 13:35:17 +01:00
Outstanding DLC branch diff (#2432)
This commit is contained in:
parent
c33f88eb05
commit
528b4e01ee
5 changed files with 131 additions and 8 deletions
|
@ -1,7 +1,8 @@
|
|||
package org.bitcoins.gui
|
||||
|
||||
import org.bitcoins.cli.Config
|
||||
import org.bitcoins.core.config.BitcoinNetwork
|
||||
import org.bitcoins.core.config._
|
||||
import org.bitcoins.crypto.DoubleSha256DigestBE
|
||||
import org.bitcoins.gui.settings.Themes
|
||||
import scalafx.beans.property.{DoubleProperty, StringProperty}
|
||||
|
||||
|
@ -34,4 +35,24 @@ object GlobalData {
|
|||
case Some(rpcPort) =>
|
||||
Config(debug = debug, rpcPort = rpcPort)
|
||||
}
|
||||
|
||||
lazy val broadcastUrl: String = GlobalData.network match {
|
||||
case MainNet =>
|
||||
"https://blockstream.info/api/tx"
|
||||
case TestNet3 =>
|
||||
"https://blockstream.info/testnet/api/tx"
|
||||
case net @ (RegTest | SigNet) => s"Broadcast from your own node on $net"
|
||||
}
|
||||
|
||||
/** Builds a url for the blockstream explorer to view the tx */
|
||||
def buildTxUrl(txid: DoubleSha256DigestBE): String = {
|
||||
network match {
|
||||
case MainNet =>
|
||||
s"https://blockstream.info/tx/${txid.hex}"
|
||||
case TestNet3 =>
|
||||
s"https://blockstream.info/testnet/tx/${txid.hex}"
|
||||
case net @ (RegTest | SigNet) =>
|
||||
s"View transaction on your own node on $net"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import org.bitcoins.core.config.MainNet
|
|||
import org.bitcoins.core.number.{Int32, UInt16, UInt32}
|
||||
import org.bitcoins.core.protocol.dlc.DLCStatus
|
||||
import org.bitcoins.core.protocol.tlv._
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.crypto.{CryptoUtil, ECPrivateKey, Sha256DigestBE}
|
||||
import org.bitcoins.gui.dlc.dialog._
|
||||
import org.bitcoins.gui.{GlobalData, TaskRunner}
|
||||
|
@ -17,11 +18,32 @@ import scalafx.scene.control.TextArea
|
|||
import scalafx.stage.Window
|
||||
import upickle.default._
|
||||
|
||||
import scala.util.{Failure, Success}
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
class DLCPaneModel(resultArea: TextArea, oracleInfoArea: TextArea) {
|
||||
var taskRunner: TaskRunner = _
|
||||
|
||||
lazy val txPrintFunc: String => String = str => {
|
||||
// See if it was an error or not
|
||||
Try(Transaction.fromHex(str)) match {
|
||||
case Failure(_) =>
|
||||
// if it was print the error
|
||||
str
|
||||
case Success(tx) =>
|
||||
s"""|TxId: ${tx.txIdBE.hex}
|
||||
|
|
||||
|url: ${GlobalData.buildTxUrl(tx.txIdBE)}
|
||||
|
|
||||
|If the tx doesn't show up after a few minutes at this url you may need to manually
|
||||
|broadcast the tx with the full hex below
|
||||
|
|
||||
|Link to broadcast: ${GlobalData.broadcastUrl}
|
||||
|
|
||||
|Transaction: ${tx.hex}
|
||||
""".stripMargin
|
||||
}
|
||||
}
|
||||
|
||||
// Sadly, it is a Java "pattern" to pass null into
|
||||
// constructors to signal that you want some default
|
||||
val parentWindow: ObjectProperty[Window] =
|
||||
|
@ -62,7 +84,8 @@ class DLCPaneModel(resultArea: TextArea, oracleInfoArea: TextArea) {
|
|||
|
||||
def printDLCDialogResult[T <: CliCommand](
|
||||
caption: String,
|
||||
dialog: DLCDialog[T]): Unit = {
|
||||
dialog: DLCDialog[T],
|
||||
postProcessStr: String => String = str => str): Unit = {
|
||||
val result = dialog.showAndWait(parentWindow.value)
|
||||
|
||||
result match {
|
||||
|
@ -71,7 +94,8 @@ class DLCPaneModel(resultArea: TextArea, oracleInfoArea: TextArea) {
|
|||
caption = caption,
|
||||
op = {
|
||||
ConsoleCli.exec(command, GlobalData.consoleCliConfig) match {
|
||||
case Success(commandReturn) => resultArea.text = commandReturn
|
||||
case Success(commandReturn) =>
|
||||
resultArea.text = postProcessStr(commandReturn)
|
||||
case Failure(err) =>
|
||||
err.printStackTrace()
|
||||
resultArea.text = s"Error executing command:\n${err.getMessage}"
|
||||
|
@ -220,15 +244,17 @@ class DLCPaneModel(resultArea: TextArea, oracleInfoArea: TextArea) {
|
|||
}
|
||||
|
||||
def onGetFunding(): Unit = {
|
||||
printDLCDialogResult("GetDLCFundingTx", new GetFundingDLCDialog)
|
||||
printDLCDialogResult("GetDLCFundingTx",
|
||||
new GetFundingDLCDialog,
|
||||
txPrintFunc)
|
||||
}
|
||||
|
||||
def onExecute(): Unit = {
|
||||
printDLCDialogResult("ExecuteDLC", new ExecuteDLCDialog)
|
||||
printDLCDialogResult("ExecuteDLC", new ExecuteDLCDialog, txPrintFunc)
|
||||
}
|
||||
|
||||
def onRefund(): Unit = {
|
||||
printDLCDialogResult("ExecuteDLCRefund", new RefundDLCDialog)
|
||||
printDLCDialogResult("ExecuteDLCRefund", new RefundDLCDialog, txPrintFunc)
|
||||
}
|
||||
|
||||
def viewDLC(status: DLCStatus): Unit = {
|
||||
|
|
|
@ -673,7 +673,7 @@ case class InputPSBTMap(elements: Vector[InputPSBTRecord])
|
|||
tx.outputs(txIn.previousOutput.vout.toInt)
|
||||
} else {
|
||||
throw new UnsupportedOperationException(
|
||||
"Not enough information in the InputPSBTMap to get a valid InputInfo")
|
||||
s"Not enough information in the InputPSBTMap to get a valid InputInfo: $elements")
|
||||
}
|
||||
|
||||
val redeemScriptOpt = finalizedScriptSigOpt match {
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
package org.bitcoins.testkit.util
|
||||
|
||||
import org.bitcoins.core.protocol.dlc.{CETSignatures, FundingSignatures}
|
||||
import org.bitcoins.core.protocol.script.{
|
||||
EmptyScriptPubKey,
|
||||
P2WPKHWitnessV0,
|
||||
P2WSHWitnessV0,
|
||||
ScriptWitnessV0
|
||||
}
|
||||
import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
|
||||
import org.bitcoins.crypto.{ECAdaptorSignature, ECDigitalSignature}
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
object BytesUtil {
|
||||
|
||||
def flipAtIndex(bytes: ByteVector, byteIndex: Int): ByteVector = {
|
||||
val (front, backWithToFlip) = bytes.splitAt(byteIndex)
|
||||
val (toFlip, back) = backWithToFlip.splitAt(1)
|
||||
front ++ toFlip.xor(ByteVector.fromByte(1)) ++ back
|
||||
}
|
||||
|
||||
def flipBit(signature: ECDigitalSignature): ECDigitalSignature = {
|
||||
ECDigitalSignature(flipAtIndex(signature.bytes, 60))
|
||||
}
|
||||
|
||||
def flipBit(partialSignature: PartialSignature): PartialSignature = {
|
||||
partialSignature.copy(signature = flipBit(partialSignature.signature))
|
||||
}
|
||||
|
||||
def flipBit(adaptorSignature: ECAdaptorSignature): ECAdaptorSignature = {
|
||||
ECAdaptorSignature(flipAtIndex(adaptorSignature.bytes, 40))
|
||||
}
|
||||
|
||||
def flipBit(witness: ScriptWitnessV0): ScriptWitnessV0 = {
|
||||
witness match {
|
||||
case p2wpkh: P2WPKHWitnessV0 =>
|
||||
P2WPKHWitnessV0(p2wpkh.pubKey, flipBit(p2wpkh.signature))
|
||||
case p2wsh: P2WSHWitnessV0 =>
|
||||
val sigOpt = p2wsh.stack.zipWithIndex.find {
|
||||
case (bytes, _) =>
|
||||
bytes.length >= 67 && bytes.length <= 73
|
||||
}
|
||||
|
||||
sigOpt match {
|
||||
case Some((sig, index)) =>
|
||||
P2WSHWitnessV0(
|
||||
EmptyScriptPubKey,
|
||||
p2wsh.stack.updated(index,
|
||||
flipBit(ECDigitalSignature(sig)).bytes))
|
||||
case None =>
|
||||
P2WSHWitnessV0(
|
||||
EmptyScriptPubKey,
|
||||
p2wsh.stack.updated(0, flipAtIndex(p2wsh.stack.head, 0)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def flipBit(fundingSigs: FundingSignatures): FundingSignatures = {
|
||||
val (firstOutPoint, witness) = fundingSigs.head
|
||||
val badWitness = flipBit(witness)
|
||||
FundingSignatures(fundingSigs.tail.toVector.+:(firstOutPoint -> badWitness))
|
||||
}
|
||||
|
||||
def flipBit(cetSigs: CETSignatures): CETSignatures = {
|
||||
val badOutcomeSigs = cetSigs.outcomeSigs.map {
|
||||
case (outcome, sig) => outcome -> flipBit(sig)
|
||||
}
|
||||
val badRefundSig = flipBit(cetSigs.refundSig)
|
||||
CETSignatures(badOutcomeSigs, badRefundSig)
|
||||
}
|
||||
}
|
|
@ -83,6 +83,11 @@ trait TxDAO[DbEntryType <: TxDB]
|
|||
|
||||
def findByTxId(txId: DoubleSha256Digest): Future[Option[DbEntryType]] =
|
||||
findByTxId(txId.flip)
|
||||
|
||||
def findByTxIdBEs(
|
||||
txIdBEs: Vector[DoubleSha256DigestBE]): Future[Vector[DbEntryType]] = {
|
||||
database.run(findByPrimaryKeys(txIdBEs).result).map(_.toVector)
|
||||
}
|
||||
}
|
||||
|
||||
case class TransactionDAO()(implicit
|
||||
|
|
Loading…
Add table
Reference in a new issue