mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-20 22:13:14 +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
|
package org.bitcoins.gui
|
||||||
|
|
||||||
import org.bitcoins.cli.Config
|
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 org.bitcoins.gui.settings.Themes
|
||||||
import scalafx.beans.property.{DoubleProperty, StringProperty}
|
import scalafx.beans.property.{DoubleProperty, StringProperty}
|
||||||
|
|
||||||
|
@ -34,4 +35,24 @@ object GlobalData {
|
||||||
case Some(rpcPort) =>
|
case Some(rpcPort) =>
|
||||||
Config(debug = debug, rpcPort = 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.number.{Int32, UInt16, UInt32}
|
||||||
import org.bitcoins.core.protocol.dlc.DLCStatus
|
import org.bitcoins.core.protocol.dlc.DLCStatus
|
||||||
import org.bitcoins.core.protocol.tlv._
|
import org.bitcoins.core.protocol.tlv._
|
||||||
|
import org.bitcoins.core.protocol.transaction.Transaction
|
||||||
import org.bitcoins.crypto.{CryptoUtil, ECPrivateKey, Sha256DigestBE}
|
import org.bitcoins.crypto.{CryptoUtil, ECPrivateKey, Sha256DigestBE}
|
||||||
import org.bitcoins.gui.dlc.dialog._
|
import org.bitcoins.gui.dlc.dialog._
|
||||||
import org.bitcoins.gui.{GlobalData, TaskRunner}
|
import org.bitcoins.gui.{GlobalData, TaskRunner}
|
||||||
|
@ -17,11 +18,32 @@ import scalafx.scene.control.TextArea
|
||||||
import scalafx.stage.Window
|
import scalafx.stage.Window
|
||||||
import upickle.default._
|
import upickle.default._
|
||||||
|
|
||||||
import scala.util.{Failure, Success}
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
||||||
class DLCPaneModel(resultArea: TextArea, oracleInfoArea: TextArea) {
|
class DLCPaneModel(resultArea: TextArea, oracleInfoArea: TextArea) {
|
||||||
var taskRunner: TaskRunner = _
|
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
|
// Sadly, it is a Java "pattern" to pass null into
|
||||||
// constructors to signal that you want some default
|
// constructors to signal that you want some default
|
||||||
val parentWindow: ObjectProperty[Window] =
|
val parentWindow: ObjectProperty[Window] =
|
||||||
|
@ -62,7 +84,8 @@ class DLCPaneModel(resultArea: TextArea, oracleInfoArea: TextArea) {
|
||||||
|
|
||||||
def printDLCDialogResult[T <: CliCommand](
|
def printDLCDialogResult[T <: CliCommand](
|
||||||
caption: String,
|
caption: String,
|
||||||
dialog: DLCDialog[T]): Unit = {
|
dialog: DLCDialog[T],
|
||||||
|
postProcessStr: String => String = str => str): Unit = {
|
||||||
val result = dialog.showAndWait(parentWindow.value)
|
val result = dialog.showAndWait(parentWindow.value)
|
||||||
|
|
||||||
result match {
|
result match {
|
||||||
|
@ -71,7 +94,8 @@ class DLCPaneModel(resultArea: TextArea, oracleInfoArea: TextArea) {
|
||||||
caption = caption,
|
caption = caption,
|
||||||
op = {
|
op = {
|
||||||
ConsoleCli.exec(command, GlobalData.consoleCliConfig) match {
|
ConsoleCli.exec(command, GlobalData.consoleCliConfig) match {
|
||||||
case Success(commandReturn) => resultArea.text = commandReturn
|
case Success(commandReturn) =>
|
||||||
|
resultArea.text = postProcessStr(commandReturn)
|
||||||
case Failure(err) =>
|
case Failure(err) =>
|
||||||
err.printStackTrace()
|
err.printStackTrace()
|
||||||
resultArea.text = s"Error executing command:\n${err.getMessage}"
|
resultArea.text = s"Error executing command:\n${err.getMessage}"
|
||||||
|
@ -220,15 +244,17 @@ class DLCPaneModel(resultArea: TextArea, oracleInfoArea: TextArea) {
|
||||||
}
|
}
|
||||||
|
|
||||||
def onGetFunding(): Unit = {
|
def onGetFunding(): Unit = {
|
||||||
printDLCDialogResult("GetDLCFundingTx", new GetFundingDLCDialog)
|
printDLCDialogResult("GetDLCFundingTx",
|
||||||
|
new GetFundingDLCDialog,
|
||||||
|
txPrintFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
def onExecute(): Unit = {
|
def onExecute(): Unit = {
|
||||||
printDLCDialogResult("ExecuteDLC", new ExecuteDLCDialog)
|
printDLCDialogResult("ExecuteDLC", new ExecuteDLCDialog, txPrintFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
def onRefund(): Unit = {
|
def onRefund(): Unit = {
|
||||||
printDLCDialogResult("ExecuteDLCRefund", new RefundDLCDialog)
|
printDLCDialogResult("ExecuteDLCRefund", new RefundDLCDialog, txPrintFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
def viewDLC(status: DLCStatus): Unit = {
|
def viewDLC(status: DLCStatus): Unit = {
|
||||||
|
|
|
@ -673,7 +673,7 @@ case class InputPSBTMap(elements: Vector[InputPSBTRecord])
|
||||||
tx.outputs(txIn.previousOutput.vout.toInt)
|
tx.outputs(txIn.previousOutput.vout.toInt)
|
||||||
} else {
|
} else {
|
||||||
throw new UnsupportedOperationException(
|
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 {
|
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]] =
|
def findByTxId(txId: DoubleSha256Digest): Future[Option[DbEntryType]] =
|
||||||
findByTxId(txId.flip)
|
findByTxId(txId.flip)
|
||||||
|
|
||||||
|
def findByTxIdBEs(
|
||||||
|
txIdBEs: Vector[DoubleSha256DigestBE]): Future[Vector[DbEntryType]] = {
|
||||||
|
database.run(findByPrimaryKeys(txIdBEs).result).map(_.toVector)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case class TransactionDAO()(implicit
|
case class TransactionDAO()(implicit
|
||||||
|
|
Loading…
Add table
Reference in a new issue