From b7c7cb8fc2cd55ded26d284af68b42cb90128b93 Mon Sep 17 00:00:00 2001 From: GreyMcCarthy <105063806+GreyMcCarthy@users.noreply.github.com> Date: Tue, 2 Aug 2022 12:55:43 -0400 Subject: [PATCH] Moving duplicate dataStructures in `cli` and `app-server` into `app-commons` (#4541) * duplicate data structures in (ConsoleCli.scala) and (ServerJsonModels.scala) moved to (Command.scala) * removed unused imports * rebase for merge conflitcts * change output type for Accept DLC * more error fixes * error fixes * fixed destintaion error * scala reformatting * send from outpoints test runs and passes * removed unnecessary braces, removed incorrect extensions on objects * scala reformatting * removed commented out code --- .../org/bitcoins/commons/rpc/Command.scala | 1646 ++++++++++++++++- .../scala/org/bitcoins/cli/ConsoleCli.scala | 301 +-- .../scala/org/bitcoins/gui/ContractGUI.scala | 33 +- .../org/bitcoins/gui/WalletGUIModel.scala | 5 +- .../org/bitcoins/gui/dialog/DebugDialog.scala | 6 +- .../org/bitcoins/gui/dialog/SendDialog.scala | 2 +- .../org/bitcoins/gui/dlc/DLCPaneModel.scala | 9 +- .../gui/dlc/dialog/AcceptOfferDialog.scala | 7 +- .../gui/dlc/dialog/CreateDLCOfferDialog.scala | 13 +- .../gui/dlc/dialog/ExecuteDLCDialog.scala | 2 +- .../gui/dlc/dialog/RefundDLCDialog.scala | 2 +- .../gui/dlc/dialog/SignDLCDialog.scala | 2 +- .../BitcoinSServerMainBitcoindTest.scala | 8 +- .../BitcoinSServerMainBitcoindTorTest.scala | 8 +- .../org/bitcoins/server/RoutesSpec.scala | 12 +- .../org/bitcoins/server/WebsocketTests.scala | 57 +- .../org/bitcoins/server/ChainRoutes.scala | 1 + .../org/bitcoins/server/CoreRoutes.scala | 1 + .../scala/org/bitcoins/server/DLCRoutes.scala | 2 +- .../bitcoins/server/ServerJsonModels.scala | 1310 +------------ .../org/bitcoins/server/WalletRoutes.scala | 6 +- 21 files changed, 1787 insertions(+), 1646 deletions(-) diff --git a/app-commons/src/main/scala/org/bitcoins/commons/rpc/Command.scala b/app-commons/src/main/scala/org/bitcoins/commons/rpc/Command.scala index 05c7f9514e..e181f07ab8 100644 --- a/app-commons/src/main/scala/org/bitcoins/commons/rpc/Command.scala +++ b/app-commons/src/main/scala/org/bitcoins/commons/rpc/Command.scala @@ -1,10 +1,30 @@ package org.bitcoins.commons.rpc +import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.LockUnspentOutputParameter +import org.bitcoins.commons.jsonmodels.cli.ContractDescriptorParser +import org.bitcoins.commons.serializers.JsonReaders import org.bitcoins.core.api.dlc.wallet.db.DLCContactDb -import org.bitcoins.crypto.Sha256Digest +import org.bitcoins.core.api.wallet.CoinSelectionAlgo +import org.bitcoins.core.crypto.{ExtPrivateKey, MnemonicCode} +import org.bitcoins.core.currency.{Bitcoins, Satoshis} +import org.bitcoins.core.hd.AddressType +import org.bitcoins.core.hd.AddressType.SegWit +import org.bitcoins.core.number.UInt32 +import org.bitcoins.core.protocol.BlockStamp.BlockHeight +import org.bitcoins.core.protocol.dlc.models.ContractDescriptor +import org.bitcoins.core.protocol.tlv._ +import org.bitcoins.core.protocol.transaction.{Transaction, TransactionOutPoint} +import org.bitcoins.core.protocol.{BitcoinAddress, BlockStamp} +import org.bitcoins.core.psbt.PSBT +import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte +import org.bitcoins.core.wallet.utxo.AddressLabelTag +import org.bitcoins.crypto._ +import scodec.bits.ByteVector +import ujson._ import java.net.{InetSocketAddress, URI} -import scala.util.{Failure, Try} +import java.nio.file.Path +import scala.util.{Failure, Success, Try} sealed trait CommandRpc @@ -20,6 +40,19 @@ trait AppServerCliCommand extends CliCommand { override def defaultPort: Int = 9999 } +trait Broadcastable extends CliCommand { + override def defaultPort: Int = 9999 +} +trait SignDLCCliCommand extends AppServerCliCommand + +trait AddDLCSigsCliCommand extends AppServerCliCommand + +trait AcceptDLCCliCommand extends AppServerCliCommand + +trait SendCliCommand extends AppServerCliCommand { + def destination: BitcoinAddress +} + trait OracleServerCliCommand extends CliCommand { override def defaultPort: Int = 9998 } @@ -31,9 +64,1428 @@ object CliCommand { } } +case class GetNewAddress(labelOpt: Option[AddressLabelTag]) + extends CliCommand + with AppServerCliCommand + +object GetNewAddress extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[GetNewAddress] = { + if (jsArr.value.length == 1) { + val labelOpt = nullToOpt(jsArr.arr.head).map { + case Str(str) => + AddressLabelTag(str) + case value: Value => + throw Value.InvalidData(value, "Expected a String") + } + + Try(GetNewAddress(labelOpt)) + } else if (jsArr.value.isEmpty) { + Success(GetNewAddress(None)) + } else { + sys.error(s"Too many argumements for GetNewAddress, got=$jsArr") + } + } +} + +case class LockUnspent( + unlock: Boolean, + outputParam: Vector[LockUnspentOutputParameter]) + extends CliCommand + with AppServerCliCommand + +object LockUnspent extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[LockUnspent] = { + jsArr.arr.toList match { + case unlockJs :: outPointsJs :: Nil => + Try { + val unlock = unlockJs.bool + val outPoints = jsToLockUnspentOutputParameters(outPointsJs).toVector + + LockUnspent(unlock, outPoints) + } + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 2")) + } + } +} + +case class LabelAddress(address: BitcoinAddress, label: AddressLabelTag) + extends CliCommand + with AppServerCliCommand + +object LabelAddress extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[LabelAddress] = { + jsArr.arr.toList match { + case addrJs :: labelJs :: Nil => + Try { + val addr = jsToBitcoinAddress(addrJs) + val label = AddressLabelTag(labelJs.str) + + LabelAddress(addr, label) + } + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 2")) + } + } +} + +case class GetAddressTags(address: BitcoinAddress) + extends CliCommand + with AppServerCliCommand + +object GetAddressTags extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[GetAddressTags] = { + jsArr.arr.toList match { + case addrJs :: Nil => + Try { + val addr = jsToBitcoinAddress(addrJs) + + GetAddressTags(addr) + } + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 1")) + } + } +} + +case class GetAddressLabel(address: BitcoinAddress) + extends CliCommand + with AppServerCliCommand + +object GetAddressLabel extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[GetAddressLabel] = { + jsArr.arr.toList match { + case addrJs :: Nil => + Try { + val addr = jsToBitcoinAddress(addrJs) + + GetAddressLabel(addr) + } + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 1")) + } + } +} + +case class DropAddressLabel(address: BitcoinAddress, label: String) + extends CliCommand + with AppServerCliCommand + +object DropAddressLabel extends ServerJsonModels { + + def fromJsArr(jsonArr: ujson.Arr): Try[DropAddressLabel] = { + jsonArr.arr.toList match { + case address :: label :: Nil => + Try { + val addr = jsToBitcoinAddress(address) + DropAddressLabel(addr, label.str) + } + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 2")) + } + } +} + +case class DropAddressLabels(address: BitcoinAddress) + extends CliCommand + with AppServerCliCommand + +object DropAddressLabels extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[DropAddressLabels] = { + jsArr.arr.toList match { + case addrJs :: Nil => + Try { + val addr = jsToBitcoinAddress(addrJs) + + DropAddressLabels(addr) + } + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 1")) + } + } +} + +case class GetBalance(isSats: Boolean) + extends CliCommand + with AppServerCliCommand + +object GetBalance extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[GetBalance] = { + require(jsArr.arr.size == 1, + s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") + + Try(GetBalance(jsArr.arr.head.bool)) + } +} + +case class GetConfirmedBalance(isSats: Boolean) + extends CliCommand + with AppServerCliCommand + +object GetConfirmedBalance extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[GetConfirmedBalance] = { + require(jsArr.arr.size == 1, + s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") + + Try(GetConfirmedBalance(jsArr.arr.head.bool)) + } +} + +case class GetUnconfirmedBalance(isSats: Boolean) + extends CliCommand + with AppServerCliCommand + +object GetUnconfirmedBalance extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[GetUnconfirmedBalance] = { + require(jsArr.arr.size == 1, + s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") + + Try(GetUnconfirmedBalance(jsArr.arr.head.bool)) + } +} + +case class GetAddressInfo(address: BitcoinAddress) + extends CliCommand + with AppServerCliCommand + +object GetAddressInfo extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[GetAddressInfo] = { + require(jsArr.arr.size == 1, + s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") + + val address = jsToBitcoinAddress(jsArr.arr.head) + + Try(GetAddressInfo(address)) + } +} + +case class SendRawTransaction(tx: Transaction) + extends CliCommand + with AppServerCliCommand + +object SendRawTransaction extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[SendRawTransaction] = { + require(jsArr.arr.size == 1, + s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") + + Try(SendRawTransaction(jsToTx(jsArr.arr.head))) + } +} + +case class KeyManagerPassphraseChange( + oldPassword: AesPassword, + newPassword: AesPassword) + extends CliCommand + with AppServerCliCommand + +object KeyManagerPassphraseChange extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[KeyManagerPassphraseChange] = { + jsArr.arr.toList match { + case oldPassJs :: newPassJs :: Nil => + Try { + val oldPass = AesPassword.fromString(oldPassJs.str) + val newPass = AesPassword.fromString(newPassJs.str) + + KeyManagerPassphraseChange(oldPass, newPass) + } + case Nil => + Failure( + new IllegalArgumentException( + "Missing old password and new password arguments")) + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 2")) + } + } +} + +case class KeyManagerPassphraseSet(password: AesPassword) + extends CliCommand + with AppServerCliCommand + +object KeyManagerPassphraseSet extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[KeyManagerPassphraseSet] = { + jsArr.arr.toList match { + case passJs :: Nil => + Try { + val pass = AesPassword.fromString(passJs.str) + + KeyManagerPassphraseSet(pass) + } + case Nil => + Failure(new IllegalArgumentException("Missing password argument")) + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 1")) + } + } +} + +case class ExportSeed( + walletNameOpt: Option[String], + passwordOpt: Option[AesPassword]) + extends CliCommand + with AppServerCliCommand + +object ExportSeed extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[ExportSeed] = Try { + val (walletNameOpt, passwordOpt) = jsToWalletNameAndPassword(jsArr) + ExportSeed(walletNameOpt, passwordOpt) + } +} + +case class MarkSeedAsBackedUp( + walletNameOpt: Option[String], + passwordOpt: Option[AesPassword]) + extends CliCommand + with AppServerCliCommand + +object MarkSeedAsBackedUp extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[MarkSeedAsBackedUp] = Try { + val (walletNameOpt, passwordOpt) = jsToWalletNameAndPassword(jsArr) + MarkSeedAsBackedUp(walletNameOpt, passwordOpt) + } +} + +case class GetSeedBackupTime( + walletNameOpt: Option[String], + passwordOpt: Option[AesPassword]) + extends CliCommand + with AppServerCliCommand + +object GetSeedBackupTime extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[GetSeedBackupTime] = Try { + val (walletNameOpt, passwordOpt) = jsToWalletNameAndPassword(jsArr) + GetSeedBackupTime(walletNameOpt, passwordOpt) + } +} + +case class ImportSeed( + walletNameOpt: Option[String], + mnemonic: MnemonicCode, + passwordOpt: Option[AesPassword]) + extends CliCommand + with AppServerCliCommand + +object ImportSeed extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[ImportSeed] = { + jsArr.arr.toList match { + case walletNameJs :: mnemonicJs :: passJs :: Nil => + Try { + val walletNameOpt = jsToStringOpt(walletNameJs) + val mnemonic = jsToMnemonics(mnemonicJs) + val pass = jsToAESPassword(passJs) + + ImportSeed(walletNameOpt, mnemonic, pass) + } + case walletNameJs :: mnemonicJs :: Nil => + Try { + val walletNameOpt = jsToStringOpt(walletNameJs) + val mnemonic = jsToMnemonics(mnemonicJs) + + ImportSeed(walletNameOpt, mnemonic, None) + } + case Nil => + Failure( + new IllegalArgumentException( + "Missing walletName, mnemonic, and password argument")) + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 3")) + } + } +} + +case class ImportXprv( + walletNameOpt: Option[String], + xprv: ExtPrivateKey, + passwordOpt: Option[AesPassword]) + extends CliCommand + with AppServerCliCommand + +object ImportXprv extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[ImportXprv] = { + jsArr.arr.toList match { + case walletNameJs :: xprvJs :: passJs :: Nil => + Try { + val walletNameOpt = jsToStringOpt(walletNameJs) + val xprv = ExtPrivateKey.fromString(xprvJs.str) + val pass = jsToAESPassword(passJs) + + ImportXprv(walletNameOpt, xprv, pass) + } + case walletNameJs :: xprvJs :: Nil => + Try { + val walletNameOpt = jsToStringOpt(walletNameJs) + val xprv = ExtPrivateKey.fromString(xprvJs.str) + + ImportXprv(walletNameOpt, xprv, None) + } + case Nil => + Failure( + new IllegalArgumentException( + "Missing walletName, xprv, and password argument")) + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 3")) + } + } +} + +case class CreateMultisig( + requiredKeys: Int, + keys: Vector[ECPublicKey], + addressType: AddressType) + extends CliCommand + with AppServerCliCommand + +object CreateMultisig extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[CreateMultisig] = { + jsArr.arr.toList match { + case requiredKeysJs :: keysJs :: addressTypeJs :: Nil => + Try { + val requiredKeys = requiredKeysJs.num.toInt + + val keys = keysJs.arr.map(value => ECPublicKey(value.str)) + + val addrType = AddressType.fromString(addressTypeJs.str) + CreateMultisig(requiredKeys, keys.toVector, addrType) + } + case requiredKeysJs :: keysJs :: Nil => + Try { + val requiredKeys = requiredKeysJs.num.toInt + + val keys = keysJs.arr.map(value => ECPublicKey(value.str)) + + CreateMultisig(requiredKeys, keys.toVector, SegWit) + } + case Nil => + Failure( + new IllegalArgumentException( + "Missing requiredKeys, keys, and addressType argument")) + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 3")) + } + } +} + +case class CombinePSBTs(psbts: Seq[PSBT]) + extends CliCommand + with AppServerCliCommand + +object CombinePSBTs extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[CombinePSBTs] = { + require(jsArr.arr.size == 1, + s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") + + Try(CombinePSBTs(jsToPSBTSeq(jsArr.arr.head))) + } +} + +case class JoinPSBTs(psbts: Seq[PSBT]) + extends CliCommand + with AppServerCliCommand + +object JoinPSBTs extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[JoinPSBTs] = { + CombinePSBTs + .fromJsArr(jsArr) + .map(combine => JoinPSBTs(combine.psbts)) + } +} + +case class FinalizePSBT(psbt: PSBT) extends CliCommand with AppServerCliCommand + +object FinalizePSBT extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[FinalizePSBT] = { + require(jsArr.arr.size == 1, + s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") + + Try(FinalizePSBT(jsToPSBT(jsArr.arr.head))) + } +} + +case class ExtractFromPSBT(psbt: PSBT) + extends CliCommand + with AppServerCliCommand + +object ExtractFromPSBT extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[ExtractFromPSBT] = { + require(jsArr.arr.size == 1, + s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") + + Try(ExtractFromPSBT(jsToPSBT(jsArr.arr.head))) + } +} + +case class ConvertToPSBT(tx: Transaction) + extends CliCommand + with AppServerCliCommand + +object ConvertToPSBT extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[ConvertToPSBT] = { + require(jsArr.arr.size == 1, + s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") + + Try(ConvertToPSBT(jsToTx(jsArr.arr.head))) + } +} + +case class GetBlockHeader(hash: DoubleSha256DigestBE) + extends CliCommand + with AppServerCliCommand + +object GetBlockHeader extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[GetBlockHeader] = + Try { + require(jsArr.arr.size == 1, + s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") + + GetBlockHeader(DoubleSha256DigestBE(jsArr.arr.head.str)) + } +} + +case class DecodeRawTransaction(tx: Transaction) + extends CliCommand + with AppServerCliCommand + +object DecodeRawTransaction extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[DecodeRawTransaction] = { + jsArr.arr.toList match { + case tx :: Nil => + Try { + DecodeRawTransaction(Transaction.fromHex(tx.str)) + } + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 1")) + } + } +} + +case class DecodePSBT(psbt: PSBT) extends CliCommand with AppServerCliCommand + +object DecodePSBT extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[DecodePSBT] = { + jsArr.arr.toList match { + case psbtJs :: Nil => + Try { + val psbt = jsToPSBT(psbtJs) + DecodePSBT(psbt) + } + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 1")) + } + } +} + +case class AnalyzePSBT(psbt: PSBT) extends CliCommand with AppServerCliCommand + +object AnalyzePSBT extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[AnalyzePSBT] = { + jsArr.arr.toList match { + case psbtJs :: Nil => + Try { + AnalyzePSBT(jsToPSBT(psbtJs)) + } + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 1")) + } + } +} + +case class Rescan( + batchSize: Option[Int], + startBlock: Option[BlockStamp], + endBlock: Option[BlockStamp], + force: Boolean, + ignoreCreationTime: Boolean) + extends CliCommand + with AppServerCliCommand + +object Rescan extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[Rescan] = { + + def parseBlockStamp(value: Value): Option[BlockStamp] = + nullToOpt(value).map { + case Str(value) => BlockStamp.fromString(value) + case Num(value) => + val int = value.toInt + if (int >= 0 && int <= Int.MaxValue) + BlockHeight(int) + else throw Value.InvalidData(value, "Expected a positive integer") + case _: Value => + throw Value.InvalidData(value, "Expected a Num or a Str") + } + + def parseInt(value: Value): Option[Int] = + nullToOpt(value).map { + case Str(value) => value.toInt + case Num(value) => value.toInt + case _: Value => + throw Value.InvalidData(value, "Expected a Num or a Str") + } + + def parseBoolean(value: Value): Boolean = + value match { + case Bool(bool) => bool + case _: Value => throw Value.InvalidData(value, "Expected a Bool") + } + + jsArr.arr.toList match { + case batchSizeJs :: startJs :: endJs :: forceJs :: ignoreCreationTimeJs :: Nil => + Try { + val batchSize = parseInt(batchSizeJs) + val start = parseBlockStamp(startJs) + val end = parseBlockStamp(endJs) + val force = parseBoolean(forceJs) + val ignoreCreationTime = parseBoolean(ignoreCreationTimeJs) + Rescan(batchSize = batchSize, + startBlock = start, + endBlock = end, + force = force, + ignoreCreationTime = ignoreCreationTime) + } + case Nil => + Failure(new IllegalArgumentException("Missing addresses")) + + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 5")) + } + } + +} + +case class GetTransaction(txId: DoubleSha256DigestBE) + extends CliCommand + with AppServerCliCommand + +object GetTransaction extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[GetTransaction] = { + require(jsArr.arr.size == 1, + s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") + + Try(GetTransaction(DoubleSha256DigestBE(jsArr.arr.head.str))) + } +} + +case class SendToAddress( + destination: BitcoinAddress, + amount: Bitcoins, + satoshisPerVirtualByte: Option[SatoshisPerVirtualByte], + noBroadcast: Boolean) + extends CliCommand + with Broadcastable + with SendCliCommand + +object SendToAddress extends ServerJsonModels { + + /// TODO do this in a more coherent fashion + // custom akka-http directive? + def fromJsArr(jsArr: ujson.Arr): Try[SendToAddress] = { + jsArr.arr.toList match { + case addrJs :: bitcoinsJs :: satsPerVBytesJs :: noBroadcastJs :: Nil => + Try { + val address = jsToBitcoinAddress(addrJs) + val bitcoins = Bitcoins(bitcoinsJs.num) + val satoshisPerVirtualByte = + nullToOpt(satsPerVBytesJs).map(satsPerVBytes => + SatoshisPerVirtualByte(Satoshis(satsPerVBytes.num.toLong))) + val noBroadcast = noBroadcastJs.bool + SendToAddress(address, bitcoins, satoshisPerVirtualByte, noBroadcast) + } + case Nil => + Failure( + new IllegalArgumentException( + "Missing address, amount, and fee rate arguments")) + + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 4")) + } + } + +} + +case class GetDLCs(contactId: Option[InetSocketAddress]) + extends CliCommand + with AppServerCliCommand + +case object GetDLCs + extends CliCommand + with AppServerCliCommand + with ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[GetDLCs] = { + jsArr.arr.toList match { + case addressJs :: Nil => + Try { + val address = jsToInetSocketAddress(addressJs) + GetDLCs(Some(address)) + } + case Nil => + Try(GetDLCs(None)) + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 1")) + } + } +} + +case class GetDLC(dlcId: Sha256Digest) + extends CliCommand + with AppServerCliCommand + +object GetDLC extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[GetDLC] = { + jsArr.arr.toList match { + case paramHashJs :: Nil => + Try { + val paramHash = Sha256Digest(paramHashJs.str) + GetDLC(paramHash) + } + case Nil => + Failure(new IllegalArgumentException("Missing paramHash argument")) + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 1")) + } + } +} + +case class CreateDLCOffer( + contractInfoTLV: ContractInfoV0TLV, + collateral: Satoshis, + feeRateOpt: Option[SatoshisPerVirtualByte], + locktimeOpt: Option[UInt32], + refundLocktime: UInt32, + externalPayoutAddressOpt: Option[BitcoinAddress], + externalChangeAddressOpt: Option[BitcoinAddress], + peerAddressOpt: Option[InetSocketAddress]) + extends CliCommand + with AppServerCliCommand + +object CreateDLCOffer extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[CreateDLCOffer] = { + + def parseParameters( + contractInfoJs: Value, + collateralJs: Value, + feeRateOptJs: Value, + locktimeJs: Value, + refundLTJs: Value, + payoutAddressJs: Value, + changeAddressJs: Value, + peerAddressJs: Value) = Try { + val contractInfoTLV = jsToContractInfoTLV(contractInfoJs) + val collateral = jsToSatoshis(collateralJs) + val feeRate = jsToSatoshisPerVirtualByteOpt(feeRateOptJs) + val locktimeJsOpt = nullToOpt(locktimeJs) + val locktimeOpt = locktimeJsOpt.map(js => jsToUInt32(js)) + val refundLT = jsToUInt32(refundLTJs) + val payoutAddressJsOpt = nullToOpt(payoutAddressJs) + val payoutAddressOpt = + payoutAddressJsOpt.map(js => jsToBitcoinAddress(js)) + val changeAddressJsOpt = nullToOpt(changeAddressJs) + val changeAddressOpt = + changeAddressJsOpt.map(js => jsToBitcoinAddress(js)) + val peerAddressJsOpt = nullToOpt(peerAddressJs) + val peerAddressOpt = peerAddressJsOpt.map(js => jsToInetSocketAddress(js)) + + CreateDLCOffer(contractInfoTLV, + collateral, + feeRate, + locktimeOpt, + refundLT, + payoutAddressOpt, + changeAddressOpt, + peerAddressOpt) + } + + jsArr.arr.toList match { + case contractInfoJs :: collateralJs :: feeRateOptJs :: locktimeJs :: refundLTJs :: Nil => + parseParameters(contractInfoJs, + collateralJs, + feeRateOptJs, + locktimeJs, + refundLTJs, + Null, + Null, + Null) + case contractInfoJs :: collateralJs :: feeRateOptJs :: locktimeJs :: refundLTJs :: payoutAddressJs :: Nil => + parseParameters(contractInfoJs, + collateralJs, + feeRateOptJs, + locktimeJs, + refundLTJs, + payoutAddressJs, + Null, + Null) + case contractInfoJs :: collateralJs :: feeRateOptJs :: locktimeJs :: refundLTJs :: payoutAddressJs :: changeAddressJs :: Nil => + parseParameters(contractInfoJs, + collateralJs, + feeRateOptJs, + locktimeJs, + refundLTJs, + payoutAddressJs, + changeAddressJs, + Null) + case contractInfoJs :: collateralJs :: feeRateOptJs :: locktimeJs :: refundLTJs :: payoutAddressJs :: changeAddressJs :: peerAddressJs :: Nil => + parseParameters(contractInfoJs, + collateralJs, + feeRateOptJs, + locktimeJs, + refundLTJs, + payoutAddressJs, + changeAddressJs, + peerAddressJs) + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 6")) + } + } +} + +case class DecodeContractInfo(contractInfo: ContractInfoV0TLV) + extends CliCommand + with AppServerCliCommand + +object DecodeContractInfo extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[DecodeContractInfo] = { + jsArr.arr.toList match { + case contractInfoJs :: Nil => + Try { + val contractInfo = ContractInfoV0TLV(contractInfoJs.str) + DecodeContractInfo(contractInfo) + } + case Nil => + Failure(new IllegalArgumentException("Missing contractInfo argument")) + + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 1")) + } + } +} + +case class DecodeOffer(offer: DLCOfferTLV) + extends CliCommand + with AppServerCliCommand + +object DecodeOffer extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[DecodeOffer] = { + jsArr.arr.toList match { + case offerJs :: Nil => + Try { + val offer: LnMessage[DLCOfferTLV] = + LnMessageFactory(DLCOfferTLV).fromHex(offerJs.str) + DecodeOffer(offer.tlv) + } match { + case Success(value) => Success(value) + case Failure(_) => + Try { + val offer = DLCOfferTLV.fromHex(offerJs.str) + DecodeOffer(offer) + } + } + case Nil => + Failure(new IllegalArgumentException("Missing offer argument")) + + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 1")) + } + } +} + +case class DecodeAnnouncement(announcement: OracleAnnouncementTLV) + extends CliCommand + with AppServerCliCommand + +object DecodeAnnouncement extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[DecodeAnnouncement] = { + jsArr.arr.toList match { + case annJs :: Nil => + Try { + val announcementTLV = OracleAnnouncementTLV(annJs.str) + DecodeAnnouncement(announcementTLV) + } + case Nil => + Failure(new IllegalArgumentException("Missing announcement argument")) + + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 1")) + } + } +} + +case class AcceptDLCOffer( + offer: LnMessage[DLCOfferTLV], + externalPayoutAddressOpt: Option[BitcoinAddress], + externalChangeAddressOpt: Option[BitcoinAddress], + peerAddress: Option[InetSocketAddress]) + extends CliCommand + with AcceptDLCCliCommand + +object AcceptDLCOffer extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[AcceptDLCOffer] = { + def parseParameters( + offerJs: Value, + payoutAddressJs: Value, + changeAddressJs: Value, + peerAddressJs: Value) = Try { + val offer = LnMessageFactory(DLCOfferTLV).fromHex(offerJs.str) + val payoutAddressJsOpt = nullToOpt(payoutAddressJs) + val payoutAddressOpt = + payoutAddressJsOpt.map(js => jsToBitcoinAddress(js)) + val changeAddressJsOpt = nullToOpt(changeAddressJs) + val changeAddressOpt = + changeAddressJsOpt.map(js => jsToBitcoinAddress(js)) + val peerAddressJsOpt = nullToOpt(peerAddressJs) + val peerAddress = peerAddressJsOpt.map(js => jsToInetSocketAddress(js)) + AcceptDLCOffer(offer, payoutAddressOpt, changeAddressOpt, peerAddress) + } + + jsArr.arr.toList match { + case offerJs :: Nil => + parseParameters(offerJs, Null, Null, Null) + case offerJs :: payoutAddressJs :: Nil => + parseParameters(offerJs, payoutAddressJs, Null, Null) + case offerJs :: payoutAddressJs :: changeAddressJs :: Nil => + parseParameters(offerJs, payoutAddressJs, changeAddressJs, Null) + case offerJs :: payoutAddressJs :: changeAddressJs :: peerAddressJs :: Nil => + parseParameters(offerJs, + payoutAddressJs, + changeAddressJs, + peerAddressJs) + case Nil => + Failure(new IllegalArgumentException("Missing offer argument")) + + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 1")) + } + } +} + +case class AcceptDLC( + offer: LnMessage[DLCOfferTLV], + peerAddr: InetSocketAddress, + externalPayoutAddressOpt: Option[BitcoinAddress], + externalChangeAddressOpt: Option[BitcoinAddress]) + extends CliCommand + with AcceptDLCCliCommand + +object AcceptDLC extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[AcceptDLC] = { + def parseParameters( + offerJs: Value, + addrJs: Value, + payoutAddressJs: Value, + changeAddressJs: Value): Try[AcceptDLC] = Try { + val lnMessageOfferT = LnMessageFactory(DLCOfferTLV) + .fromHexT(offerJs.str) + val offer: LnMessage[DLCOfferTLV] = lnMessageOfferT match { + case Success(o) => o + case Failure(_) => LnMessage(DLCOfferTLV.fromHex(offerJs.str)) + } + + val peerAddr = jsToInetSocketAddress(addrJs) + val payoutAddressJsOpt = nullToOpt(payoutAddressJs) + val payoutAddressOpt = + payoutAddressJsOpt.map(js => jsToBitcoinAddress(js)) + val changeAddressJsOpt = nullToOpt(changeAddressJs) + val changeAddressOpt = + changeAddressJsOpt.map(js => jsToBitcoinAddress(js)) + + AcceptDLC(offer, peerAddr, payoutAddressOpt, changeAddressOpt) + } + + jsArr.arr.toList match { + case offerJs :: addrJs :: Nil => + parseParameters(offerJs, addrJs, Null, Null) + case offerJs :: addrJs :: payoutAddressJs :: Nil => + parseParameters(offerJs, addrJs, payoutAddressJs, Null) + case offerJs :: addrJs :: payoutAddressJs :: changeAddressJs :: Nil => + parseParameters(offerJs, addrJs, payoutAddressJs, changeAddressJs) + case Nil => + Failure( + new IllegalArgumentException("Missing offer and peerAddr argument")) + + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 2")) + } + } +} + +case class SignDLCFromFile(path: Path, destination: Option[Path]) + extends CliCommand + with SignDLCCliCommand + +case class SignDLC(accept: LnMessage[DLCAcceptTLV]) + extends CliCommand + with SignDLCCliCommand + +object SignDLC extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[SignDLC] = { + jsArr.arr.toList match { + case acceptJs :: Nil => + Try { + val accept = LnMessageFactory(DLCAcceptTLV).fromHex(acceptJs.str) + SignDLC(accept) + } + case Nil => + Failure(new IllegalArgumentException("Missing accept argument")) + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 1")) + } + } +} + +case class AddDLCSigs(sigs: LnMessage[DLCSignTLV]) + extends CliCommand + with AddDLCSigsCliCommand + +object AddDLCSigs extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[AddDLCSigs] = { + jsArr.arr.toList match { + case sigsJs :: Nil => + Try { + val sigs = LnMessageFactory(DLCSignTLV).fromHex(sigsJs.str) + AddDLCSigs(sigs) + } + case Nil => + Failure(new IllegalArgumentException("Missing sigs argument")) + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 1")) + } + } +} + +case class GetDLCFundingTx(contractId: ByteVector) + extends CliCommand + with AppServerCliCommand + +object GetDLCFundingTx extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[GetDLCFundingTx] = { + jsArr.arr.toList match { + case contractIdJs :: Nil => + Try { + val contractId = ByteVector.fromValidHex(contractIdJs.str) + GetDLCFundingTx(contractId) + } + case Nil => + Failure(new IllegalArgumentException("Missing contractId argument")) + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 1")) + } + } +} + +case class BroadcastDLCFundingTx(contractId: ByteVector) + extends CliCommand + with AppServerCliCommand + +object BroadcastDLCFundingTx extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[BroadcastDLCFundingTx] = { + jsArr.arr.toList match { + case contractIdJs :: Nil => + Try { + val contractId = ByteVector.fromValidHex(contractIdJs.str) + BroadcastDLCFundingTx(contractId) + } + case Nil => + Failure(new IllegalArgumentException("Missing contractId argument")) + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 1")) + } + } +} + +case class ExecuteDLC( + contractId: ByteVector, + oracleSigs: Vector[OracleAttestmentTLV], + noBroadcast: Boolean) + extends CliCommand + with Broadcastable + +object ExecuteDLC extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[ExecuteDLC] = { + jsArr.arr.toList match { + case contractIdJs :: oracleSigsJs :: noBroadcastJs :: Nil => + Try { + val contractId = ByteVector.fromValidHex(contractIdJs.str) + val oracleSigs = jsToOracleAttestmentTLVVec(oracleSigsJs) + val noBroadcast = noBroadcastJs.bool + + ExecuteDLC(contractId, oracleSigs, noBroadcast) + } + case Nil => + Failure( + new IllegalArgumentException( + "Missing contractId, oracleSigs, and noBroadcast arguments")) + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 3")) + } + } +} + +case class ExecuteDLCRefund(contractId: ByteVector, noBroadcast: Boolean) + extends CliCommand + with Broadcastable + +object ExecuteDLCRefund extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[ExecuteDLCRefund] = { + jsArr.arr.toList match { + case contractIdJs :: noBroadcastJs :: Nil => + Try { + val contractId = ByteVector.fromValidHex(contractIdJs.str) + val noBroadcast = noBroadcastJs.bool + + ExecuteDLCRefund(contractId, noBroadcast) + } + case Nil => + Failure(new IllegalArgumentException("Missing contractId argument")) + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 2")) + } + } +} + +case class SendFromOutPoints( + outPoints: Vector[TransactionOutPoint], + destination: BitcoinAddress, + amount: Bitcoins, + feeRateOpt: Option[SatoshisPerVirtualByte]) + extends CliCommand + with SendCliCommand + +object SendFromOutPoints extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[SendFromOutPoints] = { + jsArr.arr.toList match { + case outPointsJs :: addrJs :: bitcoinsJs :: satsPerVBytesJs :: Nil => + Try { + val outPoints = jsToTransactionOutPointSeq(outPointsJs).toVector + val address = jsToBitcoinAddress(addrJs) + val bitcoins = Bitcoins(bitcoinsJs.num) + val satoshisPerVirtualByte = + nullToOpt(satsPerVBytesJs).map(satsPerVBytes => + SatoshisPerVirtualByte(Satoshis(satsPerVBytes.num.toLong))) + SendFromOutPoints(outPoints, + address, + bitcoins, + satoshisPerVirtualByte) + } + case Nil => + Failure( + new IllegalArgumentException( + "Missing outPoints, address, amount, and fee rate arguments")) + + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 4")) + } + } +} + +case class SweepWallet( + destination: BitcoinAddress, + feeRateOpt: Option[SatoshisPerVirtualByte]) + extends CliCommand + with SendCliCommand + with ServerJsonModels + +object SweepWallet extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[SweepWallet] = { + jsArr.arr.toList match { + case addrJs :: satsPerVBytesJs :: Nil => + Try { + val destination = jsToBitcoinAddress(addrJs) + val satoshisPerVirtualByte = + nullToOpt(satsPerVBytesJs).map(satsPerVBytes => + SatoshisPerVirtualByte(Satoshis(satsPerVBytes.num.toLong))) + SweepWallet(destination, satoshisPerVirtualByte) + } + case addrJs :: Nil => + Try { + val destination = jsToBitcoinAddress(addrJs) + SweepWallet(destination, None) + } + case Nil => + Failure( + new IllegalArgumentException( + "Missing address and fee rate arguments")) + + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 2")) + } + } +} + +case class SendWithAlgo( + destination: BitcoinAddress, + amount: Bitcoins, + feeRateOpt: Option[SatoshisPerVirtualByte], + algo: CoinSelectionAlgo) + extends CliCommand + with SendCliCommand + with ServerJsonModels + +object SendWithAlgo extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[SendWithAlgo] = { + jsArr.arr.toList match { + case addrJs :: bitcoinsJs :: satsPerVBytesJs :: algoJs :: Nil => + Try { + val address = jsToBitcoinAddress(addrJs) + val bitcoins = Bitcoins(bitcoinsJs.num) + val satoshisPerVirtualByte = + nullToOpt(satsPerVBytesJs).map(satsPerVBytes => + SatoshisPerVirtualByte(Satoshis(satsPerVBytes.num.toLong))) + val algo = jsToCoinSelectionAlgo(algoJs) + + SendWithAlgo(address, bitcoins, satoshisPerVirtualByte, algo) + } + case Nil => + Failure( + new IllegalArgumentException( + "Missing address, amount, fee rate, and algo arguments")) + + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 4")) + } + } + +} + +case class SignPSBT(psbt: PSBT) + extends CliCommand + with AppServerCliCommand + with ServerJsonModels + +object SignPSBT extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[SignPSBT] = { + require(jsArr.arr.size == 1, + s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") + + Try(SignPSBT(jsToPSBT(jsArr.arr.head))) + } +} + +case class OpReturnCommit( + message: String, + hashMessage: Boolean, + feeRateOpt: Option[SatoshisPerVirtualByte]) + extends CliCommand + with AppServerCliCommand + with ServerJsonModels + +object OpReturnCommit extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[OpReturnCommit] = { + jsArr.arr.toList match { + case messageJs :: hashMessageJs :: feeRateOptJs :: Nil => + Try { + val message = messageJs.str + val hashMessage = hashMessageJs.bool + val feeRateOpt = + nullToOpt(feeRateOptJs).map(satsPerVBytes => + SatoshisPerVirtualByte(Satoshis(satsPerVBytes.num.toLong))) + OpReturnCommit(message, hashMessage, feeRateOpt) + } + case Nil => + Failure( + new IllegalArgumentException( + "Missing message, hashMessage, and fee rate arguments")) + + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 3")) + } + } +} +case class BumpFee(txId: DoubleSha256DigestBE, feeRate: SatoshisPerVirtualByte) + +case class BumpFeeCPFP( + txId: DoubleSha256DigestBE, + feeRate: SatoshisPerVirtualByte) + extends AppServerCliCommand + +case class BumpFeeRBF( + txId: DoubleSha256DigestBE, + feeRate: SatoshisPerVirtualByte) + extends AppServerCliCommand + +object BumpFee extends ServerJsonModels { + + def fromJsArr(jsArr: ujson.Arr): Try[BumpFee] = { + jsArr.arr.toList match { + case txIdJs :: feeRateJs :: Nil => + Try { + val txId = DoubleSha256DigestBE(txIdJs.str) + val feeRate = SatoshisPerVirtualByte(Satoshis(feeRateJs.num.toLong)) + BumpFee(txId, feeRate) + } + case Nil => + Failure( + new IllegalArgumentException("Missing txId and fee rate arguments")) + + case other => + Failure( + new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 2")) + } + } +} + +case class CreateContractInfo( + announcementTLV: OracleAnnouncementTLV, + totalCollateral: Satoshis, + contractDescriptor: ContractDescriptorTLV) + extends CommandRpc + with AppServerCliCommand + with ServerJsonModels { + + def ContractDescriptorTLV: ContractDescriptor = { + ContractDescriptor.fromTLV(contractDescriptor) + } +} + +object CreateContractInfo extends ServerJsonModels { + + lazy val empty: CreateContractInfo = { + CreateContractInfo(announcementTLV = OracleAnnouncementV0TLV.dummy, + totalCollateral = Satoshis.zero, + contractDescriptor = ContractDescriptorTLV.empty) + } + + def fromJsArr(arr: ujson.Arr): Try[CreateContractInfo] = { + arr.arr.toVector match { + case announcementVal +: totalCollateralVal +: payoutsVal +: Vector() => + Try { + val announcementTLV = + OracleAnnouncementTLV.fromHex(announcementVal.str) + val totalCollateral = Satoshis(totalCollateralVal.num.toLong) + //validate that these are part of the announcement? + val contractDescriptor = + ContractDescriptorParser.parseCmdLine(payoutsVal, announcementTLV) + + CreateContractInfo(announcementTLV, + totalCollateral, + contractDescriptor) + } + case other => + val exn = new IllegalArgumentException( + s"Bad number or arguments to createcontractinfo, got=${other.length} expected=3") + Failure(exn) + } + } +} + case class ContactAdd(alias: String, address: InetSocketAddress, memo: String) extends CommandRpc - with AppServerCliCommand { + with AppServerCliCommand + with ServerJsonModels { def toDLCContactDb: DLCContactDb = DLCContactDb(alias, address, memo) } @@ -65,6 +1517,7 @@ case object ContactsList extends CommandRpc with AppServerCliCommand case class ContactRemove(address: InetSocketAddress) extends CommandRpc with AppServerCliCommand + with ServerJsonModels object ContactRemove { @@ -89,6 +1542,7 @@ object ContactRemove { case class DLCContactAdd(dlcId: Sha256Digest, address: InetSocketAddress) extends CommandRpc with AppServerCliCommand + with ServerJsonModels object DLCContactAdd { @@ -135,3 +1589,189 @@ object DLCContactRemove { } } } + +trait ServerJsonModels { + + def jsToOracleAnnouncementTLV(js: Value): OracleAnnouncementTLV = + js match { + case str: Str => + OracleAnnouncementTLV(str.value) + case _: Value => + throw Value.InvalidData( + js, + "Expected an OracleAnnouncementTLV as a hex string") + } + + def jsToContractInfoTLV(js: Value): ContractInfoV0TLV = + js match { + case str: Str => + ContractInfoV0TLV(str.value) + case _: Value => + throw Value.InvalidData(js, "Expected a ContractInfo as a hex string") + } + + def jsToSatoshisPerVirtualByteOpt(js: Value): Option[SatoshisPerVirtualByte] = + nullToOpt(js).map { + case str: Str => + SatoshisPerVirtualByte(Satoshis(str.value)) + case num: Num => + SatoshisPerVirtualByte(Satoshis(num.value.toLong)) + case _: Value => + throw Value.InvalidData(js, "Expected a fee rate in sats/vbyte") + } + + def jsToUInt32(js: Value): UInt32 = + js match { + case str: Str => + UInt32(BigInt(str.value)) + case num: Num => + UInt32(num.value.toLong) + case _: Value => + throw Value.InvalidData(js, "Expected a UInt32") + } + + def jsToSatoshis(js: Value): Satoshis = JsonReaders.jsToSatoshis(js) + + def jsToBitcoinAddress(js: Value): BitcoinAddress = { + try { + BitcoinAddress.fromString(js.str) + } catch { + case _: IllegalArgumentException => + throw Value.InvalidData(js, "Expected a valid address") + } + } + + def jsToPSBTSeq(js: Value): Seq[PSBT] = { + js.arr.foldLeft(Seq.empty[PSBT])((seq, psbt) => seq :+ jsToPSBT(psbt)) + } + + def jsToPSBT(js: Value): PSBT = PSBT.fromString(js.str) + + def jsToTransactionOutPointSeq(js: Value): Seq[TransactionOutPoint] = { + js.arr.foldLeft(Seq.empty[TransactionOutPoint])((seq, outPoint) => + seq :+ jsToTransactionOutPoint(outPoint)) + } + + def jsToTransactionOutPoint(js: Value): TransactionOutPoint = + TransactionOutPoint(js.str) + + def jsToLockUnspentOutputParameter(js: Value): LockUnspentOutputParameter = + LockUnspentOutputParameter.fromJson(js) + + def jsToLockUnspentOutputParameters( + js: Value): Seq[LockUnspentOutputParameter] = { + js.arr.foldLeft(Seq.empty[LockUnspentOutputParameter])((seq, outPoint) => + seq :+ jsToLockUnspentOutputParameter(outPoint)) + } + + def jsToCoinSelectionAlgo(js: Value): CoinSelectionAlgo = + CoinSelectionAlgo + .fromString(js.str) + + def jsToTx(js: Value): Transaction = Transaction.fromHex(js.str) + + def nullToOpt(value: Value): Option[Value] = + value match { + case Null => None + case Arr(arr) if arr.isEmpty => None + case Arr(arr) if arr.size == 1 => Some(arr.head) + case _: Value => Some(value) + } + + def jsToSchnorrDigitalSignature(js: Value): SchnorrDigitalSignature = + js match { + case str: Str => + SchnorrDigitalSignature(str.value) + case _: Value => + throw Value.InvalidData( + js, + "Expected a SchnorrDigitalSignature as a hex string") + } + + def jsToSchnorrDigitalSignatureVec( + js: Value): Vector[SchnorrDigitalSignature] = { + js.arr.foldLeft(Vector.empty[SchnorrDigitalSignature])((vec, sig) => + vec :+ jsToSchnorrDigitalSignature(sig)) + } + + def jsToOracleAttestmentTLV(js: Value): OracleAttestmentTLV = + js match { + case str: Str => + OracleAttestmentTLV(str.value) + case _: Value => + throw Value.InvalidData( + js, + "Expected a OracleAttestmentTLV as a hex string") + } + + def jsToOracleAttestmentTLVVec(js: Value): Vector[OracleAttestmentTLV] = { + js.arr.foldLeft(Vector.empty[OracleAttestmentTLV])((vec, tlv) => + vec :+ jsToOracleAttestmentTLV(tlv)) + } + + def jsToAESPassword(js: Value): Option[AesPassword] = { + js match { + case Str(str) => + Some(AesPassword.fromString(str)) + case Null => + None + case Arr(_) | False | True | Num(_) | Obj(_) => + throw new IllegalArgumentException("password must be a string or null") + } + } + + def jsToStringOpt(js: Value): Option[String] = { + js match { + case Str(str) => + Some(str) + case Null => + None + case Arr(_) | False | True | Num(_) | Obj(_) => + throw new IllegalArgumentException("password must be a string or null") + } + } + + def jsToWalletNameAndPassword( + js: Value): (Option[String], Option[AesPassword]) = { + js match { + case Arr(arr) => + arr.toList match { + case walletNameJs :: passJs :: Nil => + (jsToStringOpt(walletNameJs), jsToAESPassword(passJs)) + case walletNameJs :: Nil => + (jsToStringOpt(walletNameJs), None) + case Nil => + (None, None) + case other => + throw new IllegalArgumentException( + s"Bad number of arguments: ${other.length}. Expected: 2") + } + case _: Value => + throw new IllegalArgumentException(s"Expected json.Arr") + } + } + + def jsToMnemonics(js: Value): MnemonicCode = { + val mnemonicWords = js match { + case Str(str) => str.split(' ').toVector + case Arr(arr) => arr.map(_.str).toVector + case Null | False | True | Num(_) | Obj(_) => + throw new IllegalArgumentException( + "mnemonic must be a string or array of strings") + } + MnemonicCode.fromWords(mnemonicWords) + } + + def jsToInetSocketAddress( + js: Value, + defaultPort: Int = -1): InetSocketAddress = { + js match { + case str: Str => + val uri = new URI("tcp://" + str.str) + val port = if (uri.getPort >= 0) uri.getPort else defaultPort + InetSocketAddress.createUnresolved(uri.getHost, port) + case _: Value => + throw Value.InvalidData(js, "Expected a host address") + } + } +} diff --git a/app/cli/src/main/scala/org/bitcoins/cli/ConsoleCli.scala b/app/cli/src/main/scala/org/bitcoins/cli/ConsoleCli.scala index a78fd4a6f4..a5cc190cb0 100644 --- a/app/cli/src/main/scala/org/bitcoins/cli/ConsoleCli.scala +++ b/app/cli/src/main/scala/org/bitcoins/cli/ConsoleCli.scala @@ -3,14 +3,7 @@ package org.bitcoins.cli import org.bitcoins.cli.CliCommand._ import org.bitcoins.cli.CliReaders._ import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.LockUnspentOutputParameter -import org.bitcoins.commons.rpc.{ - AppServerCliCommand, - ContactAdd, - ContactRemove, - ContactsList, - OracleServerCliCommand, - ServerlessCliCommand -} +import org.bitcoins.commons.rpc._ import org.bitcoins.commons.serializers.Picklers._ import org.bitcoins.core.api.wallet.CoinSelectionAlgo import org.bitcoins.core.config.NetworkParameters @@ -112,14 +105,14 @@ object ConsoleCli { .action((tx, conf) => conf.copy(command = conf.command match { case decode: DecodeRawTransaction => - decode.copy(transaction = tx) + decode.copy(tx = tx) case other => other }))), note(sys.props("line.separator") + "=== Wallet ==="), cmd("rescan") .action((_, conf) => conf.copy( - command = Rescan(addressBatchSize = Option.empty, + command = Rescan(batchSize = Option.empty, startBlock = Option.empty, endBlock = Option.empty, force = false, @@ -141,7 +134,7 @@ object ConsoleCli { .action((batchSize, conf) => conf.copy(command = conf.command match { case rescan: Rescan => - rescan.copy(addressBatchSize = Option(batchSize)) + rescan.copy(batchSize = Option(batchSize)) case other => other })), opt[BlockStamp]("start") @@ -666,10 +659,10 @@ object ConsoleCli { arg[Vector[LockUnspentOutputParameter]]("transactions") .text("The transaction outpoints to unlock/lock, empty to apply to all utxos") .optional() - .action((outPoints, conf) => + .action((outputParam, conf) => conf.copy(command = conf.command match { case lockUnspent: LockUnspent => - lockUnspent.copy(outPoints = outPoints) + lockUnspent.copy(outputParam = outputParam) case other => other })) ), @@ -863,7 +856,7 @@ object ConsoleCli { .action((_, conf) => conf.copy(command = DecodeOffer(null))) .text("Decodes an offer message into json") .children( - arg[LnMessage[DLCOfferTLV]]("offer") + arg[DLCOfferTLV]("offer") .text("Hex encoded dlc offer message") .required() .action((offer, conf) => @@ -907,7 +900,10 @@ object ConsoleCli { command = CreateDLCOffer(ContractInfoV0TLV.dummy, Satoshis.zero, None, + None, UInt32.zero, + None, + None, None))) .text("Creates a DLC offer that another party can accept") .children( @@ -917,7 +913,7 @@ object ConsoleCli { .action((info, conf) => conf.copy(command = conf.command match { case offer: CreateDLCOffer => - offer.copy(contractInfo = info) + offer.copy(contractInfoTLV = info) case other => other })), arg[Satoshis]("collateral") @@ -941,10 +937,10 @@ object ConsoleCli { arg[UInt32]("refundlocktime") .text("Locktime of the refund transaction") .required() - .action((refundLT, conf) => + .action((refundLocktime, conf) => conf.copy(command = conf.command match { case offer: CreateDLCOffer => - offer.copy(refundLT = refundLT) + offer.copy(refundLocktime = refundLocktime) case other => other })), opt[UInt32]("cetlocktime") @@ -953,7 +949,7 @@ object ConsoleCli { .action((locktime, conf) => conf.copy(command = conf.command match { case offer: CreateDLCOffer => - offer.copy(cetLocktimeOpt = Some(locktime)) + offer.copy(locktimeOpt = Some(locktime)) case other => other })) ), @@ -961,7 +957,9 @@ object ConsoleCli { .action((_, conf) => conf.copy(command = AcceptDLC(null, - InetSocketAddress.createUnresolved("localhost", 0)))) + InetSocketAddress.createUnresolved("localhost", 0), + None, + None))) .text("Accepts a DLC offer given from another party") .children( arg[LnMessage[DLCOfferTLV]]("offer") @@ -984,7 +982,8 @@ object ConsoleCli { })) ), cmd("acceptdlcoffer") - .action((_, conf) => conf.copy(command = AcceptDLCOffer(null))) + .action((_, conf) => + conf.copy(command = AcceptDLCOffer(null, None, None, None))) .text("Accepts a DLC offer given from another party") .children( arg[LnMessage[DLCOfferTLV]]("offer") @@ -1293,8 +1292,8 @@ object ConsoleCli { .required() .action((ann, conf) => conf.copy(command = conf.command match { - case create: CreateContractInfo => - create.copy(announcementTLV = ann) + case createContractInfo: CreateContractInfo => + createContractInfo.copy(announcementTLV = ann) case other => other })), arg[Satoshis]("totalCollateral") @@ -1306,7 +1305,7 @@ object ConsoleCli { create.copy(totalCollateral = totalCollateral) case other => other })), - arg[ujson.Value]("contractDescriptor") + arg[ContractDescriptorTLV]("contractDescriptor") .text("The contract descriptor in the DLC. This is expected to be of format [[outcome1, payout1], [outcome2, payout2], ...]") .required() .action((contractDescriptor, conf) => @@ -1479,7 +1478,7 @@ object ConsoleCli { .action((tx, conf) => conf.copy(command = conf.command match { case convertToPSBT: ConvertToPSBT => - convertToPSBT.copy(transaction = tx) + convertToPSBT.copy(tx = tx) case other => other })) ), @@ -1953,25 +1952,45 @@ object ConsoleCli { case GetDLCs => RequestParam("getdlcs") case GetDLC(dlcId) => RequestParam("getdlc", Seq(up.writeJs(dlcId))) - case CreateDLCOffer(contractInfo, + case CreateDLCOffer(contractInfoTLV, collateral, feeRateOpt, - refundLT, - cetLocktimeOpt) => + locktimeOpt, + refundLocktime, + externalPayoutAddressOpt, + externalChangeAddressOpt, + peerAddressOpt) => RequestParam( "createdlcoffer", Seq( - up.writeJs(contractInfo), + up.writeJs(contractInfoTLV), up.writeJs(collateral), up.writeJs(feeRateOpt), - up.writeJs(cetLocktimeOpt), - up.writeJs(refundLT) + up.writeJs(locktimeOpt), + up.writeJs(refundLocktime), + up.writeJs(externalPayoutAddressOpt), + up.writeJs(externalChangeAddressOpt), + up.writeJs(peerAddressOpt) ) ) - case AcceptDLC(offer, address) => - RequestParam("acceptdlc", Seq(up.writeJs(offer), up.writeJs(address))) - case AcceptDLCOffer(offer) => - RequestParam("acceptdlcoffer", Seq(up.writeJs(offer))) + case AcceptDLC(offer, + peerAddr, + externalPayoutAddressOpt, + externalChangeAddressOpt) => + RequestParam("acceptdlc", + Seq(up.writeJs(offer), + up.writeJs(peerAddr), + up.writeJs(externalPayoutAddressOpt), + up.writeJs(externalChangeAddressOpt))) + case AcceptDLCOffer(offer, + peerAddr, + externalPayoutAddressOpt, + externalChangeAddressOpt) => + RequestParam("acceptdlcoffer", + Seq(up.writeJs(offer), + up.writeJs(peerAddr), + up.writeJs(externalPayoutAddressOpt), + up.writeJs(externalChangeAddressOpt))) case AcceptDLCOfferFromFile(path, dest) => RequestParam("acceptdlcofferfromfile", Seq(up.writeJs(path), up.writeJs(dest))) @@ -2248,7 +2267,7 @@ object ConsoleCli { RequestParam("offer-remove", args) case cmd @ (_: ServerlessCliCommand | _: AppServerCliCommand | - _: OracleServerCliCommand) => + _: Broadcastable | _: OracleServerCliCommand) => sys.error(s"Command $cmd unsupported") case org.bitcoins.commons.rpc.CliCommand.NoCommand => ??? } @@ -2359,10 +2378,6 @@ object Config { object CliCommand { - trait Broadcastable { - def noBroadcast: Boolean - } - case object GetVersion extends ServerlessCliCommand case object GetInfo extends AppServerCliCommand @@ -2370,51 +2385,18 @@ object CliCommand { // DLC case object GetDLCHostAddress extends AppServerCliCommand - case class DecodeContractInfo(contractInfo: ContractInfoV0TLV) - extends AppServerCliCommand - - case class DecodeOffer(offer: LnMessage[DLCOfferTLV]) - extends AppServerCliCommand - - case class DecodeAnnouncement(announcement: OracleAnnouncementV0TLV) - extends AppServerCliCommand - case class DecodeAttestments(sigs: OracleAttestmentV0TLV) extends AppServerCliCommand - case class CreateDLCOffer( - contractInfo: ContractInfoV0TLV, - collateral: Satoshis, - feeRateOpt: Option[SatoshisPerVirtualByte], - refundLT: UInt32, - cetLocktimeOpt: Option[UInt32]) - extends AppServerCliCommand - sealed trait AcceptDLCCliCommand extends AppServerCliCommand - case class AcceptDLC( - offer: LnMessage[DLCOfferTLV], - peerAddr: InetSocketAddress) - extends AcceptDLCCliCommand - - case class AcceptDLCOffer(offer: LnMessage[DLCOfferTLV]) - extends AcceptDLCCliCommand - case class AcceptDLCOfferFromFile(path: Path, destination: Option[Path]) extends AcceptDLCCliCommand sealed trait SignDLCCliCommand extends AppServerCliCommand - case class SignDLC(accept: LnMessage[DLCAcceptTLV]) extends SignDLCCliCommand - - case class SignDLCFromFile(path: Path, destination: Option[Path]) - extends SignDLCCliCommand - sealed trait AddDLCSigsCliCommand extends AppServerCliCommand - case class AddDLCSigs(sigs: LnMessage[DLCSignTLV]) - extends AddDLCSigsCliCommand - case class AddDLCSigsFromFile(path: Path) extends AddDLCSigsCliCommand sealed trait AddDLCSigsAndBroadcastCliCommand extends AddDLCSigsCliCommand @@ -2425,42 +2407,8 @@ object CliCommand { case class AddDLCSigsAndBroadcastFromFile(path: Path) extends AddDLCSigsAndBroadcastCliCommand - case class GetDLCFundingTx(contractId: ByteVector) extends AppServerCliCommand - - case class BroadcastDLCFundingTx(contractId: ByteVector) - extends AppServerCliCommand - - case class ExecuteDLC( - contractId: ByteVector, - oracleSigs: Vector[OracleAttestmentTLV], - noBroadcast: Boolean) - extends AppServerCliCommand - with Broadcastable - - case class ExecuteDLCRefund(contractId: ByteVector, noBroadcast: Boolean) - extends AppServerCliCommand - with Broadcastable - case class CancelDLC(dlcId: Sha256Digest) extends AppServerCliCommand - case object GetDLCs extends AppServerCliCommand - case class GetDLC(dlcId: Sha256Digest) extends AppServerCliCommand - - case class CreateContractInfo( - announcementTLV: OracleAnnouncementTLV, - totalCollateral: Satoshis, - contractDescriptor: ujson.Value) - extends AppServerCliCommand - - object CreateContractInfo { - - lazy val empty: CreateContractInfo = { - CreateContractInfo(announcementTLV = OracleAnnouncementV0TLV.dummy, - totalCollateral = Satoshis.zero, - contractDescriptor = ujson.Null) - } - } - case class AddDLCOffer( offer: LnMessage[DLCOfferTLV], peer: String, @@ -2469,79 +2417,9 @@ object CliCommand { case class RemoveDLCOffer(offerHash: Sha256Digest) extends AppServerCliCommand - sealed trait SendCliCommand extends AppServerCliCommand { - def destination: BitcoinAddress - } - // Wallet - case class SendToAddress( - destination: BitcoinAddress, - amount: Bitcoins, - satoshisPerVirtualByte: Option[SatoshisPerVirtualByte], - noBroadcast: Boolean) - extends SendCliCommand - with Broadcastable - - case class SendFromOutPoints( - outPoints: Vector[TransactionOutPoint], - destination: BitcoinAddress, - amount: Bitcoins, - feeRateOpt: Option[SatoshisPerVirtualByte]) - extends SendCliCommand - - case class SweepWallet( - destination: BitcoinAddress, - feeRateOpt: Option[SatoshisPerVirtualByte]) - extends SendCliCommand - - case class SendWithAlgo( - destination: BitcoinAddress, - amount: Bitcoins, - feeRateOpt: Option[SatoshisPerVirtualByte], - algo: CoinSelectionAlgo) - extends SendCliCommand - - case class OpReturnCommit( - message: String, - hashMessage: Boolean, - feeRateOpt: Option[SatoshisPerVirtualByte]) - extends AppServerCliCommand - - case class BumpFeeCPFP( - txId: DoubleSha256DigestBE, - feeRate: SatoshisPerVirtualByte) - extends AppServerCliCommand - - case class BumpFeeRBF( - txId: DoubleSha256DigestBE, - feeRate: SatoshisPerVirtualByte) - extends AppServerCliCommand - - case class SignPSBT(psbt: PSBT) extends AppServerCliCommand - - case class LockUnspent( - unlock: Boolean, - outPoints: Vector[LockUnspentOutputParameter]) - extends AppServerCliCommand - - case class LabelAddress(address: BitcoinAddress, label: AddressLabelTag) - extends AppServerCliCommand - - case class GetAddressTags(address: BitcoinAddress) extends AppServerCliCommand - - case class GetAddressLabel(address: BitcoinAddress) - extends AppServerCliCommand case object GetAddressLabels extends AppServerCliCommand - - case class DropAddressLabel(address: BitcoinAddress, label: String) - extends AppServerCliCommand - - case class DropAddressLabels(address: BitcoinAddress) - extends AppServerCliCommand - - case class GetNewAddress(labelOpt: Option[AddressLabelTag]) - extends AppServerCliCommand case object GetUtxos extends AppServerCliCommand case object ListReservedUtxos extends AppServerCliCommand case object GetAddresses extends AppServerCliCommand @@ -2552,55 +2430,14 @@ object CliCommand { case object CreateNewAccount extends AppServerCliCommand case object IsEmpty extends AppServerCliCommand case object WalletInfo extends AppServerCliCommand - case class GetBalance(isSats: Boolean) extends AppServerCliCommand - case class GetConfirmedBalance(isSats: Boolean) extends AppServerCliCommand - case class GetUnconfirmedBalance(isSats: Boolean) extends AppServerCliCommand + case class GetBalances(isSats: Boolean) extends AppServerCliCommand - case class GetAddressInfo(address: BitcoinAddress) extends AppServerCliCommand + case object GetDLCWalletAccounting extends AppServerCliCommand - case class GetTransaction(txId: DoubleSha256DigestBE) - extends AppServerCliCommand - - case class KeyManagerPassphraseChange( - oldPassword: AesPassword, - newPassword: AesPassword) - extends AppServerCliCommand - - case class KeyManagerPassphraseSet(password: AesPassword) - extends AppServerCliCommand - - case class ImportSeed( - walletNameOpt: Option[String], - mnemonic: MnemonicCode, - passwordOpt: Option[AesPassword]) - extends AppServerCliCommand - - case class ExportSeed( - walletNameOpt: Option[String], - passwordOpt: Option[AesPassword]) - extends AppServerCliCommand - - case class MarkSeedAsBackedUp( - walletNameOpt: Option[String], - passwordOpt: Option[AesPassword]) - extends AppServerCliCommand - - case class GetSeedBackupTime( - walletNameOpt: Option[String], - passwordOpt: Option[AesPassword]) - extends AppServerCliCommand - - case class ImportXprv( - walletNameOpt: Option[String], - xprv: ExtPrivateKey, - passwordOpt: Option[AesPassword]) - extends AppServerCliCommand - // Node case object GetPeers extends AppServerCliCommand case object Stop extends AppServerCliCommand - case class SendRawTransaction(tx: Transaction) extends AppServerCliCommand // Chain case object GetBestBlockHash extends AppServerCliCommand @@ -2608,37 +2445,11 @@ object CliCommand { case object GetFilterCount extends AppServerCliCommand case object GetFilterHeaderCount extends AppServerCliCommand - case class GetBlockHeader(hash: DoubleSha256DigestBE) - extends AppServerCliCommand - case object GetMedianTimePast extends AppServerCliCommand - case class DecodeRawTransaction(transaction: Transaction) - extends AppServerCliCommand - - case class Rescan( - addressBatchSize: Option[Int], - startBlock: Option[BlockStamp], - endBlock: Option[BlockStamp], - force: Boolean, - ignoreCreationTime: Boolean) - extends AppServerCliCommand - // PSBT - case class DecodePSBT(psbt: PSBT) extends AppServerCliCommand - case class CombinePSBTs(psbts: Seq[PSBT]) extends AppServerCliCommand - case class JoinPSBTs(psbts: Seq[PSBT]) extends AppServerCliCommand - case class FinalizePSBT(psbt: PSBT) extends AppServerCliCommand - case class ExtractFromPSBT(psbt: PSBT) extends AppServerCliCommand - case class ConvertToPSBT(transaction: Transaction) extends AppServerCliCommand - case class AnalyzePSBT(psbt: PSBT) extends AppServerCliCommand // Util - case class CreateMultisig( - requiredKeys: Int, - keys: Vector[ECPublicKey], - addressType: AddressType) - extends AppServerCliCommand case class ZipDataDir(path: Path) extends AppServerCliCommand diff --git a/app/gui/src/main/scala/org/bitcoins/gui/ContractGUI.scala b/app/gui/src/main/scala/org/bitcoins/gui/ContractGUI.scala index d4c081dc65..37db857001 100644 --- a/app/gui/src/main/scala/org/bitcoins/gui/ContractGUI.scala +++ b/app/gui/src/main/scala/org/bitcoins/gui/ContractGUI.scala @@ -1,44 +1,21 @@ package org.bitcoins.gui -import org.bitcoins.cli.CliCommand.{ +import org.bitcoins.cli.CliCommand.AddDLCSigsAndBroadcastCliCommand +import org.bitcoins.commons.rpc.{ AcceptDLCCliCommand, - AddDLCSigsAndBroadcastCliCommand, CreateDLCOffer, SignDLCCliCommand } import org.bitcoins.core.protocol.dlc.models.DLCStatus -import org.bitcoins.core.protocol.tlv.{ - ContractInfoV0TLV, - DLCAcceptTLV, - DLCOfferTLV, - DLCSignTLV, - LnMessageFactory, - OracleAnnouncementV0TLV -} +import org.bitcoins.core.protocol.tlv._ import org.bitcoins.gui.contract.GlobalContractData import org.bitcoins.gui.dlc.DLCPaneModel -import org.bitcoins.gui.dlc.dialog.{ - AcceptOfferDialog, - BroadcastDLCDialog, - CreateDLCOfferDialog, - DLCDialogContainer, - SignDLCDialog, - ViewDLCDialog -} +import org.bitcoins.gui.dlc.dialog._ import org.bitcoins.gui.util.GUIUtil import scalafx.beans.property.StringProperty import scalafx.geometry._ import scalafx.scene.Parent -import scalafx.scene.control.{ - ContextMenu, - Hyperlink, - Label, - MenuItem, - TableColumn, - TableView, - TextArea, - TextField -} +import scalafx.scene.control._ import scalafx.scene.layout._ import java.io.File diff --git a/app/gui/src/main/scala/org/bitcoins/gui/WalletGUIModel.scala b/app/gui/src/main/scala/org/bitcoins/gui/WalletGUIModel.scala index c61d561238..e287a4f289 100644 --- a/app/gui/src/main/scala/org/bitcoins/gui/WalletGUIModel.scala +++ b/app/gui/src/main/scala/org/bitcoins/gui/WalletGUIModel.scala @@ -4,6 +4,8 @@ import akka.actor.{ActorSystem, Cancellable} import grizzled.slf4j.Logging import org.bitcoins.cli.CliCommand._ import org.bitcoins.cli.ConsoleCli +import org.bitcoins.cli.ConsoleCli.exec +import org.bitcoins.commons.rpc.GetNewAddress import org.bitcoins.core.config.DLC import org.bitcoins.core.dlc.accounting.RateOfReturnUtil import org.bitcoins.core.serializers.PicklerKeys @@ -60,8 +62,7 @@ class WalletGUIModel(dlcModel: DLCPaneModel)(implicit system: ActorSystem) taskRunner.run( caption = "Get New Address", op = { - ConsoleCli.exec(GetNewAddress(None), - GlobalData.consoleCliConfig) match { + exec(GetNewAddress(None), GlobalData.consoleCliConfig) match { case Success(commandReturn) => addressP.success(commandReturn) case Failure(err) => addressP.failure(err) diff --git a/app/gui/src/main/scala/org/bitcoins/gui/dialog/DebugDialog.scala b/app/gui/src/main/scala/org/bitcoins/gui/dialog/DebugDialog.scala index 02d24b7de3..afc2d9cbfd 100644 --- a/app/gui/src/main/scala/org/bitcoins/gui/dialog/DebugDialog.scala +++ b/app/gui/src/main/scala/org/bitcoins/gui/dialog/DebugDialog.scala @@ -1,8 +1,8 @@ package org.bitcoins.gui.dialog import grizzled.slf4j.Logging -import org.bitcoins.cli.CliCommand.{LockUnspent, Rescan} import org.bitcoins.cli.ConsoleCli +import org.bitcoins.commons.rpc.{LockUnspent, Rescan} import org.bitcoins.gui.{GlobalData, TaskRunner} import scalafx.Includes._ import scalafx.geometry.Pos @@ -56,7 +56,7 @@ object DebugDialog extends Logging { unreserveAllUTXOsButton.onAction = _ => { taskRunner.run( "Unreserve All UTXOs", { - ConsoleCli.exec(LockUnspent(true, Vector.empty), + ConsoleCli.exec(LockUnspent(unlock = true, Vector.empty), GlobalData.consoleCliConfig) match { case Success(_) => () case Failure(err) => @@ -107,7 +107,7 @@ object DebugDialog extends Logging { private def setRescanAction(taskRunner: TaskRunner, button: Button): Unit = { val rescanCmd = { - Rescan(addressBatchSize = Some(200), + Rescan(batchSize = Some(200), startBlock = None, endBlock = None, force = true, diff --git a/app/gui/src/main/scala/org/bitcoins/gui/dialog/SendDialog.scala b/app/gui/src/main/scala/org/bitcoins/gui/dialog/SendDialog.scala index 54438ebaeb..8b5adaa396 100644 --- a/app/gui/src/main/scala/org/bitcoins/gui/dialog/SendDialog.scala +++ b/app/gui/src/main/scala/org/bitcoins/gui/dialog/SendDialog.scala @@ -1,6 +1,6 @@ package org.bitcoins.gui.dialog -import org.bitcoins.cli.CliCommand.{SendCliCommand, SendToAddress, SweepWallet} +import org.bitcoins.commons.rpc.{SendCliCommand, SendToAddress, SweepWallet} import org.bitcoins.core.currency._ import org.bitcoins.core.protocol.BitcoinAddress import org.bitcoins.core.wallet.fee.{FeeUnit, SatoshisPerVirtualByte} diff --git a/app/gui/src/main/scala/org/bitcoins/gui/dlc/DLCPaneModel.scala b/app/gui/src/main/scala/org/bitcoins/gui/dlc/DLCPaneModel.scala index f19898de37..14a11a921b 100644 --- a/app/gui/src/main/scala/org/bitcoins/gui/dlc/DLCPaneModel.scala +++ b/app/gui/src/main/scala/org/bitcoins/gui/dlc/DLCPaneModel.scala @@ -3,7 +3,14 @@ package org.bitcoins.gui.dlc import grizzled.slf4j.Logging import org.bitcoins.cli.CliCommand._ import org.bitcoins.cli.ConsoleCli -import org.bitcoins.commons.rpc.CliCommand +import org.bitcoins.commons.rpc.{ + BroadcastDLCFundingTx, + CliCommand, + GetDLC, + GetDLCs, + GetTransaction, + SendRawTransaction +} import org.bitcoins.commons.serializers.Picklers._ import org.bitcoins.core.protocol.dlc.models._ import org.bitcoins.core.protocol.transaction.Transaction diff --git a/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/AcceptOfferDialog.scala b/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/AcceptOfferDialog.scala index 47f6224421..0b75c2a674 100644 --- a/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/AcceptOfferDialog.scala +++ b/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/AcceptOfferDialog.scala @@ -1,6 +1,6 @@ package org.bitcoins.gui.dlc.dialog -import org.bitcoins.cli.CliCommand._ +import org.bitcoins.commons.rpc.{AcceptDLC, AcceptDLCCliCommand, AcceptDLCOffer} import org.bitcoins.core.config.DLC import org.bitcoins.core.protocol.dlc.models._ import org.bitcoins.core.protocol.tlv._ @@ -25,10 +25,9 @@ class AcceptOfferDialog extends CliCommandProducer[AcceptDLCCliCommand] { val text = peerAddressTF.text.value.trim if (text.nonEmpty) { val peer = NetworkUtil.parseInetSocketAddress(text, DLC.DefaultPort) - - AcceptDLC(offer, peer) + AcceptDLC(offer = offer, peerAddr = peer, None, None) } else { - AcceptDLCOffer(offer) + AcceptDLCOffer(offer = offer, None, None, None) } } diff --git a/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/CreateDLCOfferDialog.scala b/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/CreateDLCOfferDialog.scala index 303cc94a60..cf253b0dfd 100644 --- a/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/CreateDLCOfferDialog.scala +++ b/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/CreateDLCOfferDialog.scala @@ -1,7 +1,7 @@ package org.bitcoins.gui.dlc.dialog import grizzled.slf4j.Logging -import org.bitcoins.cli.CliCommand.CreateDLCOffer +import org.bitcoins.commons.rpc.CreateDLCOffer import org.bitcoins.core.currency._ import org.bitcoins.core.number.UInt32 import org.bitcoins.core.protocol.dlc.models._ @@ -573,7 +573,7 @@ class CreateDLCOfferDialog(feeRate: FeeUnit) } else None } - val contractInfo = oracleInfo match { + val contractInfoTLV = oracleInfo match { case oracleInfo: EnumOracleInfo => val missingOutcomes = fields.values.filter(_._2.text.value.isEmpty) if (missingOutcomes.nonEmpty) { @@ -606,11 +606,14 @@ class CreateDLCOfferDialog(feeRate: FeeUnit) } CreateDLCOffer( - contractInfo = contractInfo, + contractInfoTLV = contractInfoTLV, collateral = collateral, feeRateOpt = feeRateOpt, - refundLT = refundLocktime, - cetLocktimeOpt = None + refundLocktime = refundLocktime, + locktimeOpt = None, + externalPayoutAddressOpt = None, + externalChangeAddressOpt = None, + peerAddressOpt = None ) } } diff --git a/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/ExecuteDLCDialog.scala b/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/ExecuteDLCDialog.scala index 4c598968ea..db01e424f9 100644 --- a/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/ExecuteDLCDialog.scala +++ b/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/ExecuteDLCDialog.scala @@ -1,6 +1,6 @@ package org.bitcoins.gui.dlc.dialog -import org.bitcoins.cli.CliCommand.ExecuteDLC +import org.bitcoins.commons.rpc.ExecuteDLC import org.bitcoins.core.protocol.tlv.OracleAttestmentTLV import scalafx.scene.Node import scalafx.scene.control.TextField diff --git a/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/RefundDLCDialog.scala b/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/RefundDLCDialog.scala index c8f8a20722..caf8cc2f02 100644 --- a/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/RefundDLCDialog.scala +++ b/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/RefundDLCDialog.scala @@ -1,6 +1,6 @@ package org.bitcoins.gui.dlc.dialog -import org.bitcoins.cli.CliCommand.ExecuteDLCRefund +import org.bitcoins.commons.rpc.ExecuteDLCRefund import scalafx.scene.Node import scalafx.scene.control.TextField import scodec.bits.ByteVector diff --git a/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/SignDLCDialog.scala b/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/SignDLCDialog.scala index 8d1aa5e9da..9e13b9b1aa 100644 --- a/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/SignDLCDialog.scala +++ b/app/gui/src/main/scala/org/bitcoins/gui/dlc/dialog/SignDLCDialog.scala @@ -1,7 +1,7 @@ package org.bitcoins.gui.dlc.dialog import grizzled.slf4j.Logging -import org.bitcoins.cli.CliCommand._ +import org.bitcoins.commons.rpc.{SignDLC, SignDLCCliCommand, SignDLCFromFile} import org.bitcoins.core.protocol.dlc.models._ import org.bitcoins.core.protocol.tlv._ import org.bitcoins.gui.GlobalData diff --git a/app/server-test/src/test/scala/org/bitcoins/server/BitcoinSServerMainBitcoindTest.scala b/app/server-test/src/test/scala/org/bitcoins/server/BitcoinSServerMainBitcoindTest.scala index 7448a271dc..53c839837a 100644 --- a/app/server-test/src/test/scala/org/bitcoins/server/BitcoinSServerMainBitcoindTest.scala +++ b/app/server-test/src/test/scala/org/bitcoins/server/BitcoinSServerMainBitcoindTest.scala @@ -1,6 +1,8 @@ package org.bitcoins.server import org.bitcoins.asyncutil.AsyncUtil +import org.bitcoins.cli.ConsoleCli.exec +import org.bitcoins.commons.rpc.{GetBalance, GetNewAddress} import org.bitcoins.cli.{CliCommand, Config, ConsoleCli} import org.bitcoins.commons.util.ServerArgParser import org.bitcoins.testkit.fixtures.BitcoinSAppConfigBitcoinFixtureNotStarted @@ -24,10 +26,8 @@ class BitcoinSServerMainBitcoindTest _ <- server.start() info = ConsoleCli.exec(CliCommand.WalletInfo, cliConfig) - balance = ConsoleCli.exec(CliCommand.GetBalance(isSats = true), - cliConfig) - addr = ConsoleCli.exec(CliCommand.GetNewAddress(labelOpt = None), - cliConfig) + balance = exec(GetBalance(isSats = true), cliConfig) + addr = exec(GetNewAddress(labelOpt = None), cliConfig) blockHash = ConsoleCli.exec(CliCommand.GetBestBlockHash, cliConfig) _ <- AsyncUtil.nonBlockingSleep(1.second) _ <- server.stop() //stop to free all resources diff --git a/app/server-test/src/test/scala/org/bitcoins/server/BitcoinSServerMainBitcoindTorTest.scala b/app/server-test/src/test/scala/org/bitcoins/server/BitcoinSServerMainBitcoindTorTest.scala index 6556c1ee06..38eb20db8d 100644 --- a/app/server-test/src/test/scala/org/bitcoins/server/BitcoinSServerMainBitcoindTorTest.scala +++ b/app/server-test/src/test/scala/org/bitcoins/server/BitcoinSServerMainBitcoindTorTest.scala @@ -1,7 +1,9 @@ package org.bitcoins.server import org.bitcoins.asyncutil.AsyncUtil +import org.bitcoins.cli.ConsoleCli.exec import org.bitcoins.cli.{CliCommand, Config, ConsoleCli} +import org.bitcoins.commons.rpc.{GetBalance, GetNewAddress} import org.bitcoins.commons.util.ServerArgParser import org.bitcoins.testkit.fixtures.BitcoinSAppConfigBitcoinFixtureNotStarted import org.bitcoins.testkit.tor.CachedTor @@ -27,10 +29,8 @@ class BitcoinSServerMainBitcoindTorTest _ <- server.start() info = ConsoleCli.exec(CliCommand.WalletInfo, cliConfig) - balance = ConsoleCli.exec(CliCommand.GetBalance(isSats = true), - cliConfig) - addr = ConsoleCli.exec(CliCommand.GetNewAddress(labelOpt = None), - cliConfig) + balance = exec(GetBalance(isSats = true), cliConfig) + addr = exec(GetNewAddress(labelOpt = None), cliConfig) blockHash = ConsoleCli.exec(CliCommand.GetBestBlockHash, cliConfig) _ <- AsyncUtil.nonBlockingSleep(1.second) _ <- server.stop() //stop to free all resources diff --git a/app/server-test/src/test/scala/org/bitcoins/server/RoutesSpec.scala b/app/server-test/src/test/scala/org/bitcoins/server/RoutesSpec.scala index 243072c4b2..aa66a7c3b3 100644 --- a/app/server-test/src/test/scala/org/bitcoins/server/RoutesSpec.scala +++ b/app/server-test/src/test/scala/org/bitcoins/server/RoutesSpec.scala @@ -59,18 +59,18 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory { // the genesis address val testAddressStr = "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" - val testAddress = BitcoinAddress.fromString(testAddressStr) + val testAddress: BitcoinAddress = BitcoinAddress.fromString(testAddressStr) val testLabel: AddressLabelTag = AddressLabelTag("test") - val mockWalletApi = mock[MockWalletApi] + val mockWalletApi: MockWalletApi = mock[MockWalletApi] - val mockChainApi = mock[ChainApi] + val mockChainApi: ChainApi = mock[ChainApi] - val mockNode = mock[Node] + val mockNode: Node = mock[Node] - val chainRoutes = ChainRoutes(mockChainApi, RegTest, Future.unit) + val chainRoutes: ChainRoutes = ChainRoutes(mockChainApi, RegTest, Future.unit) - val nodeRoutes = NodeRoutes(mockNode) + val nodeRoutes: NodeRoutes = NodeRoutes(mockNode) val walletRoutes: WalletRoutes = WalletRoutes(mockWalletApi)(system, conf.walletConf) diff --git a/app/server-test/src/test/scala/org/bitcoins/server/WebsocketTests.scala b/app/server-test/src/test/scala/org/bitcoins/server/WebsocketTests.scala index b89b29d145..73bf5ee2af 100644 --- a/app/server-test/src/test/scala/org/bitcoins/server/WebsocketTests.scala +++ b/app/server-test/src/test/scala/org/bitcoins/server/WebsocketTests.scala @@ -10,6 +10,7 @@ import akka.http.scaladsl.model.ws.{ } import akka.http.scaladsl.model.{HttpHeader, StatusCodes} import akka.stream.scaladsl.{Flow, Keep, Sink, Source} +import org.bitcoins.cli.ConsoleCli.exec import org.bitcoins.cli.{CliCommand, Config, ConsoleCli} import org.bitcoins.commons.jsonmodels.ws.ChainNotification.{ BlockProcessedNotification, @@ -22,6 +23,14 @@ import org.bitcoins.commons.jsonmodels.ws.{ WalletWsType, WsNotification } +import org.bitcoins.commons.rpc.{ + GetBlockHeader, + GetNewAddress, + GetTransaction, + LockUnspent, + Rescan, + SendToAddress +} import org.bitcoins.commons.serializers.{Picklers, WsPicklers} import org.bitcoins.core.currency.Bitcoins import org.bitcoins.core.protocol.BitcoinAddress @@ -94,8 +103,7 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture { val cliConfig = Config(rpcPortOpt = Some(server.conf.rpcPort), rpcPassword = "wrong password") - val cliResponse = - ConsoleCli.exec(CliCommand.GetNewAddress(labelOpt = None), cliConfig) + val cliResponse = exec(GetNewAddress(labelOpt = None), cliConfig) assert(cliResponse.isFailure) assert( @@ -121,9 +129,8 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture { notificationsF._2._1 val promise: Promise[Option[Message]] = notificationsF._2._2 - val expectedAddressStr = ConsoleCli - .exec(CliCommand.GetNewAddress(labelOpt = None), cliConfig) - .get + val expectedAddressStr = + exec(GetNewAddress(labelOpt = None), cliConfig).get val expectedAddress = BitcoinAddress.fromString(expectedAddressStr) for { @@ -157,14 +164,14 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture { for { address <- addressF - cmd = CliCommand.SendToAddress(destination = address, - amount = Bitcoins.one, - satoshisPerVirtualByte = - Some(SatoshisPerVirtualByte.one), - noBroadcast = false) + cmd = SendToAddress(destination = address, + amount = Bitcoins.one, + satoshisPerVirtualByte = + Some(SatoshisPerVirtualByte.one), + noBroadcast = false) txIdStr = ConsoleCli.exec(cmd, cliConfig) expectedTxId = DoubleSha256DigestBE.fromHex(txIdStr.get) - getTxCmd = CliCommand.GetTransaction(expectedTxId) + getTxCmd = GetTransaction(expectedTxId) expectedTxStr = ConsoleCli.exec(getTxCmd, cliConfig) expectedTx = Transaction.fromHex(expectedTxStr.get) _ <- AkkaUtil.nonBlockingSleep(500.millis) @@ -196,14 +203,14 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture { for { address <- addressF - cmd = CliCommand.SendToAddress(destination = address, - amount = Bitcoins.one, - satoshisPerVirtualByte = - Some(SatoshisPerVirtualByte.one), - noBroadcast = false) + cmd = SendToAddress(destination = address, + amount = Bitcoins.one, + satoshisPerVirtualByte = + Some(SatoshisPerVirtualByte.one), + noBroadcast = false) txIdStr = ConsoleCli.exec(cmd, cliConfig) expectedTxId = DoubleSha256DigestBE.fromHex(txIdStr.get) - getTxCmd = CliCommand.GetTransaction(expectedTxId) + getTxCmd = GetTransaction(expectedTxId) expectedTxStr = ConsoleCli.exec(getTxCmd, cliConfig) expectedTx = Transaction.fromHex(expectedTxStr.get) _ <- AkkaUtil.nonBlockingSleep(500.millis) @@ -236,7 +243,7 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture { for { address <- addressF hashes <- bitcoind.generateToAddress(1, address) - cmd = CliCommand.GetBlockHeader(hash = hashes.head) + cmd = GetBlockHeader(hash = hashes.head) getBlockHeaderResultStr = ConsoleCli.exec(cmd, cliConfig) getBlockHeaderResult = upickle.default.read(getBlockHeaderResultStr.get)( Picklers.getBlockHeaderResultPickler) @@ -268,11 +275,11 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture { val promise = tuple._2._2 //lock all utxos - val lockCmd = CliCommand.LockUnspent(unlock = false, Vector.empty) + val lockCmd = LockUnspent(unlock = false, Vector.empty) ConsoleCli.exec(lockCmd, cliConfig) //unlock all utxos - val unlockCmd = CliCommand.LockUnspent(unlock = true, Vector.empty) + val unlockCmd = LockUnspent(unlock = true, Vector.empty) ConsoleCli.exec(unlockCmd, cliConfig) for { @@ -387,11 +394,11 @@ class WebsocketTests extends BitcoinSServerMainBitcoindFixture { } val notificationsF = tuple._2._1 val promise = tuple._2._2 - val cmd = CliCommand.Rescan(addressBatchSize = None, - startBlock = None, - endBlock = None, - force = true, - ignoreCreationTime = false) + val cmd = Rescan(batchSize = None, + startBlock = None, + endBlock = None, + force = true, + ignoreCreationTime = false) val _ = ConsoleCli.exec(cmd, cliConfig) for { _ <- AkkaUtil.nonBlockingSleep(5000.millis) diff --git a/app/server/src/main/scala/org/bitcoins/server/ChainRoutes.scala b/app/server/src/main/scala/org/bitcoins/server/ChainRoutes.scala index bab42ab8c3..18acb5b3fd 100644 --- a/app/server/src/main/scala/org/bitcoins/server/ChainRoutes.scala +++ b/app/server/src/main/scala/org/bitcoins/server/ChainRoutes.scala @@ -4,6 +4,7 @@ import akka.actor.ActorSystem import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server._ import org.bitcoins.commons.jsonmodels.BitcoinSServerInfo +import org.bitcoins.commons.rpc.GetBlockHeader import org.bitcoins.commons.serializers.Picklers import org.bitcoins.commons.serializers.Picklers._ import org.bitcoins.core.api.chain.ChainApi diff --git a/app/server/src/main/scala/org/bitcoins/server/CoreRoutes.scala b/app/server/src/main/scala/org/bitcoins/server/CoreRoutes.scala index 095a2468b7..66cd5f90b2 100644 --- a/app/server/src/main/scala/org/bitcoins/server/CoreRoutes.scala +++ b/app/server/src/main/scala/org/bitcoins/server/CoreRoutes.scala @@ -11,6 +11,7 @@ import org.bitcoins.core.protocol.script.{ P2SHScriptPubKey, P2WSHWitnessSPKV0 } +import org.bitcoins.commons.rpc._ import org.bitcoins.core.protocol.{Bech32Address, P2SHAddress} import org.bitcoins.core.psbt.PSBT import org.bitcoins.server.routes.{Server, ServerCommand, ServerRoute} diff --git a/app/server/src/main/scala/org/bitcoins/server/DLCRoutes.scala b/app/server/src/main/scala/org/bitcoins/server/DLCRoutes.scala index 6ef3e46cb7..65259bb19e 100644 --- a/app/server/src/main/scala/org/bitcoins/server/DLCRoutes.scala +++ b/app/server/src/main/scala/org/bitcoins/server/DLCRoutes.scala @@ -62,7 +62,7 @@ case class DLCRoutes(dlcNode: DLCNodeApi)(implicit system: ActorSystem) EnumSingleOracleInfo(create.announcementTLV) } val contractInfo = SingleContractInfo(create.totalCollateral, - create.contractDescriptor, + create.ContractDescriptorTLV, oracleInfo) Server.httpSuccess(contractInfo.hex) } diff --git a/app/server/src/main/scala/org/bitcoins/server/ServerJsonModels.scala b/app/server/src/main/scala/org/bitcoins/server/ServerJsonModels.scala index 4de5630e0b..273e0ad73b 100644 --- a/app/server/src/main/scala/org/bitcoins/server/ServerJsonModels.scala +++ b/app/server/src/main/scala/org/bitcoins/server/ServerJsonModels.scala @@ -1,25 +1,18 @@ package org.bitcoins.server import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.LockUnspentOutputParameter -import org.bitcoins.commons.jsonmodels.cli.ContractDescriptorParser import org.bitcoins.commons.serializers.JsonReaders import org.bitcoins.core.api.wallet.CoinSelectionAlgo import org.bitcoins.core.config.DLC import org.bitcoins.core.crypto._ -import org.bitcoins.core.currency.{Bitcoins, Satoshis} -import org.bitcoins.core.hd.AddressType -import org.bitcoins.core.hd.AddressType.SegWit +import org.bitcoins.core.currency.Satoshis import org.bitcoins.core.number.UInt32 -import org.bitcoins.core.protocol.BlockStamp.BlockHeight -import org.bitcoins.core.protocol.dlc.models.ContractDescriptor +import org.bitcoins.core.protocol.BitcoinAddress import org.bitcoins.core.protocol.tlv._ import org.bitcoins.core.protocol.transaction.{Transaction, TransactionOutPoint} -import org.bitcoins.core.protocol.{BitcoinAddress, BlockStamp} import org.bitcoins.core.psbt.PSBT import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte -import org.bitcoins.core.wallet.utxo.AddressLabelTag import org.bitcoins.crypto._ -import scodec.bits.ByteVector import ujson._ import java.io.File @@ -27,838 +20,6 @@ import java.net.{InetSocketAddress, URI} import java.nio.file.Path import scala.util._ -case class GetNewAddress(labelOpt: Option[AddressLabelTag]) - -object GetNewAddress extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[GetNewAddress] = { - if (jsArr.value.length == 1) { - val labelOpt = nullToOpt(jsArr.arr.head).map { - case Str(str) => - AddressLabelTag(str) - case value: Value => - throw Value.InvalidData(value, "Expected a String") - } - - Try(GetNewAddress(labelOpt)) - } else if (jsArr.value.isEmpty) { - Success(GetNewAddress(None)) - } else { - sys.error(s"Too many argumements for GetNewAddress, got=$jsArr") - } - } -} - -case class LockUnspent( - unlock: Boolean, - outputParam: Vector[LockUnspentOutputParameter]) - -object LockUnspent extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[LockUnspent] = { - jsArr.arr.toList match { - case unlockJs :: outPointsJs :: Nil => - Try { - val unlock = unlockJs.bool - val outPoints = jsToLockUnspentOutputParameters(outPointsJs).toVector - - LockUnspent(unlock, outPoints) - } - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 2")) - } - } -} - -case class LabelAddress(address: BitcoinAddress, label: AddressLabelTag) - -object LabelAddress extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[LabelAddress] = { - jsArr.arr.toList match { - case addrJs :: labelJs :: Nil => - Try { - val addr = jsToBitcoinAddress(addrJs) - val label = AddressLabelTag(labelJs.str) - - LabelAddress(addr, label) - } - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 2")) - } - } -} - -case class GetAddressTags(address: BitcoinAddress) - -object GetAddressTags extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[GetAddressTags] = { - jsArr.arr.toList match { - case addrJs :: Nil => - Try { - val addr = jsToBitcoinAddress(addrJs) - - GetAddressTags(addr) - } - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 1")) - } - } -} - -case class GetAddressLabel(address: BitcoinAddress) - -object GetAddressLabel extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[GetAddressLabel] = { - jsArr.arr.toList match { - case addrJs :: Nil => - Try { - val addr = jsToBitcoinAddress(addrJs) - - GetAddressLabel(addr) - } - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 1")) - } - } -} - -case class DropAddressLabel(address: BitcoinAddress, label: String) - -object DropAddressLabel extends ServerJsonModels { - - def fromJsArr(jsonArr: ujson.Arr): Try[DropAddressLabel] = { - jsonArr.arr.toList match { - case address :: label :: Nil => - Try { - val addr = jsToBitcoinAddress(address) - DropAddressLabel(addr, label.str) - } - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 2")) - } - } -} - -case class DropAddressLabels(address: BitcoinAddress) - -object DropAddressLabels extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[DropAddressLabels] = { - jsArr.arr.toList match { - case addrJs :: Nil => - Try { - val addr = jsToBitcoinAddress(addrJs) - - DropAddressLabels(addr) - } - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 1")) - } - } -} - -case class GetBalance(isSats: Boolean) - -object GetBalance extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[GetBalance] = { - require(jsArr.arr.size == 1, - s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") - - Try(GetBalance(jsArr.arr.head.bool)) - } -} - -case class GetConfirmedBalance(isSats: Boolean) - -object GetConfirmedBalance extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[GetConfirmedBalance] = { - require(jsArr.arr.size == 1, - s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") - - Try(GetConfirmedBalance(jsArr.arr.head.bool)) - } -} - -case class GetUnconfirmedBalance(isSats: Boolean) - -object GetUnconfirmedBalance extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[GetUnconfirmedBalance] = { - require(jsArr.arr.size == 1, - s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") - - Try(GetUnconfirmedBalance(jsArr.arr.head.bool)) - } -} - -case class GetAddressInfo(address: BitcoinAddress) - -object GetAddressInfo extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[GetAddressInfo] = { - require(jsArr.arr.size == 1, - s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") - - val address = jsToBitcoinAddress(jsArr.arr.head) - - Try(GetAddressInfo(address)) - } -} - -case class SendRawTransaction(tx: Transaction) - -object SendRawTransaction extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[SendRawTransaction] = { - require(jsArr.arr.size == 1, - s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") - - Try(SendRawTransaction(jsToTx(jsArr.arr.head))) - } -} - -case class KeyManagerPassphraseChange( - oldPassword: AesPassword, - newPassword: AesPassword) - -object KeyManagerPassphraseChange extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[KeyManagerPassphraseChange] = { - jsArr.arr.toList match { - case oldPassJs :: newPassJs :: Nil => - Try { - val oldPass = AesPassword.fromString(oldPassJs.str) - val newPass = AesPassword.fromString(newPassJs.str) - - KeyManagerPassphraseChange(oldPass, newPass) - } - case Nil => - Failure( - new IllegalArgumentException( - "Missing old password and new password arguments")) - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 2")) - } - } -} - -case class KeyManagerPassphraseSet(password: AesPassword) - -object KeyManagerPassphraseSet extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[KeyManagerPassphraseSet] = { - jsArr.arr.toList match { - case passJs :: Nil => - Try { - val pass = AesPassword.fromString(passJs.str) - - KeyManagerPassphraseSet(pass) - } - case Nil => - Failure(new IllegalArgumentException("Missing password argument")) - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 1")) - } - } -} - -case class ExportSeed( - walletNameOpt: Option[String], - passwordOpt: Option[AesPassword]) - -object ExportSeed extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[ExportSeed] = Try { - val (walletNameOpt, passwordOpt) = jsToWalletNameAndPassword(jsArr) - ExportSeed(walletNameOpt, passwordOpt) - } -} - -case class MarkSeedAsBackedUp( - walletNameOpt: Option[String], - passwordOpt: Option[AesPassword]) - -object MarkSeedAsBackedUp extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[MarkSeedAsBackedUp] = Try { - val (walletNameOpt, passwordOpt) = jsToWalletNameAndPassword(jsArr) - MarkSeedAsBackedUp(walletNameOpt, passwordOpt) - } -} - -case class GetSeedBackupTime( - walletNameOpt: Option[String], - passwordOpt: Option[AesPassword]) - -object GetSeedBackupTime extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[GetSeedBackupTime] = Try { - val (walletNameOpt, passwordOpt) = jsToWalletNameAndPassword(jsArr) - GetSeedBackupTime(walletNameOpt, passwordOpt) - } -} - -case class ImportSeed( - walletNameOpt: Option[String], - mnemonic: MnemonicCode, - passwordOpt: Option[AesPassword]) - -object ImportSeed extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[ImportSeed] = { - jsArr.arr.toList match { - case walletNameJs :: mnemonicJs :: passJs :: Nil => - Try { - val walletNameOpt = jsToStringOpt(walletNameJs) - val mnemonic = jsToMnemonics(mnemonicJs) - val pass = jsToAESPassword(passJs) - - ImportSeed(walletNameOpt, mnemonic, pass) - } - case walletNameJs :: mnemonicJs :: Nil => - Try { - val walletNameOpt = jsToStringOpt(walletNameJs) - val mnemonic = jsToMnemonics(mnemonicJs) - - ImportSeed(walletNameOpt, mnemonic, None) - } - case Nil => - Failure( - new IllegalArgumentException( - "Missing walletName, mnemonic, and password argument")) - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 3")) - } - } -} - -case class ImportXprv( - walletNameOpt: Option[String], - xprv: ExtPrivateKey, - passwordOpt: Option[AesPassword]) - -object ImportXprv extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[ImportXprv] = { - jsArr.arr.toList match { - case walletNameJs :: xprvJs :: passJs :: Nil => - Try { - val walletNameOpt = jsToStringOpt(walletNameJs) - val xprv = ExtPrivateKey.fromString(xprvJs.str) - val pass = jsToAESPassword(passJs) - - ImportXprv(walletNameOpt, xprv, pass) - } - case walletNameJs :: xprvJs :: Nil => - Try { - val walletNameOpt = jsToStringOpt(walletNameJs) - val xprv = ExtPrivateKey.fromString(xprvJs.str) - - ImportXprv(walletNameOpt, xprv, None) - } - case Nil => - Failure( - new IllegalArgumentException( - "Missing walletName, xprv, and password argument")) - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 3")) - } - } -} - -case class CreateMultisig( - requiredKeys: Int, - keys: Vector[ECPublicKey], - addressType: AddressType) - -object CreateMultisig extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[CreateMultisig] = { - jsArr.arr.toList match { - case requiredKeysJs :: keysJs :: addressTypeJs :: Nil => - Try { - val requiredKeys = requiredKeysJs.num.toInt - - val keys = keysJs.arr.map(value => ECPublicKey(value.str)) - - val addrType = AddressType.fromString(addressTypeJs.str) - CreateMultisig(requiredKeys, keys.toVector, addrType) - } - case requiredKeysJs :: keysJs :: Nil => - Try { - val requiredKeys = requiredKeysJs.num.toInt - - val keys = keysJs.arr.map(value => ECPublicKey(value.str)) - - CreateMultisig(requiredKeys, keys.toVector, SegWit) - } - case Nil => - Failure( - new IllegalArgumentException( - "Missing requiredKeys, keys, and addressType argument")) - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 3")) - } - } -} - -case class CombinePSBTs(psbts: Seq[PSBT]) - -object CombinePSBTs extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[CombinePSBTs] = { - require(jsArr.arr.size == 1, - s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") - - Try(CombinePSBTs(jsToPSBTSeq(jsArr.arr.head))) - } -} - -case class JoinPSBTs(psbts: Seq[PSBT]) - -object JoinPSBTs extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[JoinPSBTs] = { - CombinePSBTs - .fromJsArr(jsArr) - .map(combine => JoinPSBTs(combine.psbts)) - } -} - -case class FinalizePSBT(psbt: PSBT) - -object FinalizePSBT extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[FinalizePSBT] = { - require(jsArr.arr.size == 1, - s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") - - Try(FinalizePSBT(jsToPSBT(jsArr.arr.head))) - } -} - -case class ExtractFromPSBT(psbt: PSBT) - -object ExtractFromPSBT extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[ExtractFromPSBT] = { - require(jsArr.arr.size == 1, - s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") - - Try(ExtractFromPSBT(jsToPSBT(jsArr.arr.head))) - } -} - -case class ConvertToPSBT(tx: Transaction) - -object ConvertToPSBT extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[ConvertToPSBT] = { - require(jsArr.arr.size == 1, - s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") - - Try(ConvertToPSBT(jsToTx(jsArr.arr.head))) - } -} - -case class GetBlockHeader(hash: DoubleSha256DigestBE) - -object GetBlockHeader extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[GetBlockHeader] = - Try { - require(jsArr.arr.size == 1, - s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") - - GetBlockHeader(DoubleSha256DigestBE(jsArr.arr.head.str)) - } -} - -case class DecodeRawTransaction(tx: Transaction) - -object DecodeRawTransaction extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[DecodeRawTransaction] = { - jsArr.arr.toList match { - case tx :: Nil => - Try { - DecodeRawTransaction(Transaction.fromHex(tx.str)) - } - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 1")) - } - } -} - -case class DecodePSBT(psbt: PSBT) - -object DecodePSBT extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[DecodePSBT] = { - jsArr.arr.toList match { - case psbtJs :: Nil => - Try { - val psbt = jsToPSBT(psbtJs) - DecodePSBT(psbt) - } - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 1")) - } - } -} - -case class AnalyzePSBT(psbt: PSBT) - -object AnalyzePSBT extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[AnalyzePSBT] = { - jsArr.arr.toList match { - case psbtJs :: Nil => - Try { - AnalyzePSBT(jsToPSBT(psbtJs)) - } - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 1")) - } - } -} - -case class Rescan( - batchSize: Option[Int], - startBlock: Option[BlockStamp], - endBlock: Option[BlockStamp], - force: Boolean, - ignoreCreationTime: Boolean) - -object Rescan extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[Rescan] = { - - def parseBlockStamp(value: Value): Option[BlockStamp] = - nullToOpt(value).map { - case Str(value) => BlockStamp.fromString(value) - case Num(value) => - val int = value.toInt - if (int >= 0 && int <= Int.MaxValue) - BlockHeight(int) - else throw Value.InvalidData(value, "Expected a positive integer") - case _: Value => - throw Value.InvalidData(value, "Expected a Num or a Str") - } - - def parseInt(value: Value): Option[Int] = - nullToOpt(value).map { - case Str(value) => value.toInt - case Num(value) => value.toInt - case _: Value => - throw Value.InvalidData(value, "Expected a Num or a Str") - } - - def parseBoolean(value: Value): Boolean = - value match { - case Bool(bool) => bool - case _: Value => throw Value.InvalidData(value, "Expected a Bool") - } - - jsArr.arr.toList match { - case batchSizeJs :: startJs :: endJs :: forceJs :: ignoreCreationTimeJs :: Nil => - Try { - val batchSize = parseInt(batchSizeJs) - val start = parseBlockStamp(startJs) - val end = parseBlockStamp(endJs) - val force = parseBoolean(forceJs) - val ignoreCreationTime = parseBoolean(ignoreCreationTimeJs) - Rescan(batchSize = batchSize, - startBlock = start, - endBlock = end, - force = force, - ignoreCreationTime = ignoreCreationTime) - } - case Nil => - Failure(new IllegalArgumentException("Missing addresses")) - - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 5")) - } - } - -} - -case class GetTransaction(txId: DoubleSha256DigestBE) - -object GetTransaction extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[GetTransaction] = { - require(jsArr.arr.size == 1, - s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") - - Try(GetTransaction(DoubleSha256DigestBE(jsArr.arr.head.str))) - } -} - -trait Broadcastable { - def noBroadcast: Boolean -} - -case class SendToAddress( - address: BitcoinAddress, - amount: Bitcoins, - satoshisPerVirtualByte: Option[SatoshisPerVirtualByte], - noBroadcast: Boolean) - extends Broadcastable - -object SendToAddress extends ServerJsonModels { - - /// TODO do this in a more coherent fashion - // custom akka-http directive? - def fromJsArr(jsArr: ujson.Arr): Try[SendToAddress] = { - jsArr.arr.toList match { - case addrJs :: bitcoinsJs :: satsPerVBytesJs :: noBroadcastJs :: Nil => - Try { - val address = jsToBitcoinAddress(addrJs) - val bitcoins = Bitcoins(bitcoinsJs.num) - val satoshisPerVirtualByte = - nullToOpt(satsPerVBytesJs).map(satsPerVBytes => - SatoshisPerVirtualByte(Satoshis(satsPerVBytes.num.toLong))) - val noBroadcast = noBroadcastJs.bool - SendToAddress(address, bitcoins, satoshisPerVirtualByte, noBroadcast) - } - case Nil => - Failure( - new IllegalArgumentException( - "Missing address, amount, and fee rate arguments")) - - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 4")) - } - } - -} - -case class GetDLCs(contactId: Option[InetSocketAddress]) - -object GetDLCs extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[GetDLCs] = { - jsArr.arr.toList match { - case addressJs :: Nil => - Try { - val address = jsToInetSocketAddress(addressJs) - GetDLCs(Some(address)) - } - case Nil => - Try(GetDLCs(None)) - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 1")) - } - } -} - -case class GetDLC(dlcId: Sha256Digest) - -object GetDLC extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[GetDLC] = { - jsArr.arr.toList match { - case paramHashJs :: Nil => - Try { - val paramHash = Sha256Digest(paramHashJs.str) - GetDLC(paramHash) - } - case Nil => - Failure(new IllegalArgumentException("Missing paramHash argument")) - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 1")) - } - } -} - -case class CreateDLCOffer( - contractInfoTLV: ContractInfoV0TLV, - collateral: Satoshis, - feeRateOpt: Option[SatoshisPerVirtualByte], - locktimeOpt: Option[UInt32], - refundLocktime: UInt32, - externalPayoutAddressOpt: Option[BitcoinAddress], - externalChangeAddressOpt: Option[BitcoinAddress], - peerAddressOpt: Option[InetSocketAddress]) - -object CreateDLCOffer extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[CreateDLCOffer] = { - - def parseParameters( - contractInfoJs: Value, - collateralJs: Value, - feeRateOptJs: Value, - locktimeJs: Value, - refundLTJs: Value, - payoutAddressJs: Value, - changeAddressJs: Value, - peerAddressJs: Value) = Try { - val contractInfoTLV = jsToContractInfoTLV(contractInfoJs) - val collateral = jsToSatoshis(collateralJs) - val feeRate = jsToSatoshisPerVirtualByteOpt(feeRateOptJs) - val locktimeJsOpt = nullToOpt(locktimeJs) - val locktimeOpt = locktimeJsOpt.map(js => jsToUInt32(js)) - val refundLT = jsToUInt32(refundLTJs) - val payoutAddressJsOpt = nullToOpt(payoutAddressJs) - val payoutAddressOpt = - payoutAddressJsOpt.map(js => jsToBitcoinAddress(js)) - val changeAddressJsOpt = nullToOpt(changeAddressJs) - val changeAddressOpt = - changeAddressJsOpt.map(js => jsToBitcoinAddress(js)) - val peerAddressJsOpt = nullToOpt(peerAddressJs) - val peerAddressOpt = peerAddressJsOpt.map(js => jsToInetSocketAddress(js)) - - CreateDLCOffer(contractInfoTLV, - collateral, - feeRate, - locktimeOpt, - refundLT, - payoutAddressOpt, - changeAddressOpt, - peerAddressOpt) - } - - jsArr.arr.toList match { - case contractInfoJs :: collateralJs :: feeRateOptJs :: locktimeJs :: refundLTJs :: Nil => - parseParameters(contractInfoJs, - collateralJs, - feeRateOptJs, - locktimeJs, - refundLTJs, - Null, - Null, - Null) - case contractInfoJs :: collateralJs :: feeRateOptJs :: locktimeJs :: refundLTJs :: payoutAddressJs :: Nil => - parseParameters(contractInfoJs, - collateralJs, - feeRateOptJs, - locktimeJs, - refundLTJs, - payoutAddressJs, - Null, - Null) - case contractInfoJs :: collateralJs :: feeRateOptJs :: locktimeJs :: refundLTJs :: payoutAddressJs :: changeAddressJs :: Nil => - parseParameters(contractInfoJs, - collateralJs, - feeRateOptJs, - locktimeJs, - refundLTJs, - payoutAddressJs, - changeAddressJs, - Null) - case contractInfoJs :: collateralJs :: feeRateOptJs :: locktimeJs :: refundLTJs :: payoutAddressJs :: changeAddressJs :: peerAddressJs :: Nil => - parseParameters(contractInfoJs, - collateralJs, - feeRateOptJs, - locktimeJs, - refundLTJs, - payoutAddressJs, - changeAddressJs, - peerAddressJs) - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 6")) - } - } -} - -case class DecodeContractInfo(contractInfo: ContractInfoV0TLV) - -object DecodeContractInfo extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[DecodeContractInfo] = { - jsArr.arr.toList match { - case contractInfoJs :: Nil => - Try { - val contractInfo = ContractInfoV0TLV(contractInfoJs.str) - DecodeContractInfo(contractInfo) - } - case Nil => - Failure(new IllegalArgumentException("Missing contractInfo argument")) - - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 1")) - } - } -} - -case class DecodeOffer(offer: DLCOfferTLV) - -object DecodeOffer extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[DecodeOffer] = { - jsArr.arr.toList match { - case offerJs :: Nil => - Try { - val offer: LnMessage[DLCOfferTLV] = - LnMessageFactory(DLCOfferTLV).fromHex(offerJs.str) - DecodeOffer(offer.tlv) - } match { - case Success(value) => Success(value) - case Failure(_) => - Try { - val offer = DLCOfferTLV.fromHex(offerJs.str) - DecodeOffer(offer) - } - } - case Nil => - Failure(new IllegalArgumentException("Missing offer argument")) - - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 1")) - } - } -} - case class DecodeAccept(accept: DLCAcceptTLV) object DecodeAccept extends ServerJsonModels { @@ -919,28 +80,6 @@ object DecodeSign extends ServerJsonModels { } } -case class DecodeAnnouncement(announcement: OracleAnnouncementTLV) - -object DecodeAnnouncement extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[DecodeAnnouncement] = { - jsArr.arr.toList match { - case annJs :: Nil => - Try { - val announcementTLV = OracleAnnouncementTLV(annJs.str) - DecodeAnnouncement(announcementTLV) - } - case Nil => - Failure(new IllegalArgumentException("Missing announcement argument")) - - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 1")) - } - } -} - case class DecodeAttestations(announcement: OracleAttestmentV0TLV) object DecodeAttestations extends ServerJsonModels { @@ -963,148 +102,6 @@ object DecodeAttestations extends ServerJsonModels { } } -case class AcceptDLCOffer( - offer: LnMessage[DLCOfferTLV], - externalPayoutAddressOpt: Option[BitcoinAddress], - externalChangeAddressOpt: Option[BitcoinAddress], - peerAddress: Option[InetSocketAddress]) - -object AcceptDLCOffer extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[AcceptDLCOffer] = { - def parseParameters( - offerJs: Value, - payoutAddressJs: Value, - changeAddressJs: Value, - peerAddressJs: Value) = Try { - val offer = LnMessageFactory(DLCOfferTLV).fromHex(offerJs.str) - val payoutAddressJsOpt = nullToOpt(payoutAddressJs) - val payoutAddressOpt = - payoutAddressJsOpt.map(js => jsToBitcoinAddress(js)) - val changeAddressJsOpt = nullToOpt(changeAddressJs) - val changeAddressOpt = - changeAddressJsOpt.map(js => jsToBitcoinAddress(js)) - val peerAddressJsOpt = nullToOpt(peerAddressJs) - val peerAddress = peerAddressJsOpt.map(js => jsToInetSocketAddress(js)) - AcceptDLCOffer(offer, payoutAddressOpt, changeAddressOpt, peerAddress) - } - - jsArr.arr.toList match { - case offerJs :: Nil => - parseParameters(offerJs, Null, Null, Null) - case offerJs :: payoutAddressJs :: Nil => - parseParameters(offerJs, payoutAddressJs, Null, Null) - case offerJs :: payoutAddressJs :: changeAddressJs :: Nil => - parseParameters(offerJs, payoutAddressJs, changeAddressJs, Null) - case offerJs :: payoutAddressJs :: changeAddressJs :: peerAddressJs :: Nil => - parseParameters(offerJs, - payoutAddressJs, - changeAddressJs, - peerAddressJs) - case Nil => - Failure(new IllegalArgumentException("Missing offer argument")) - - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 1")) - } - } -} - -case class AcceptDLC( - offer: LnMessage[DLCOfferTLV], - peerAddr: InetSocketAddress, - externalPayoutAddressOpt: Option[BitcoinAddress], - externalChangeAddressOpt: Option[BitcoinAddress]) - -object AcceptDLC extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[AcceptDLC] = { - def parseParameters( - offerJs: Value, - addrJs: Value, - payoutAddressJs: Value, - changeAddressJs: Value): Try[AcceptDLC] = Try { - val lnMessageOfferT = LnMessageFactory(DLCOfferTLV) - .fromHexT(offerJs.str) - val offer: LnMessage[DLCOfferTLV] = lnMessageOfferT match { - case Success(o) => o - case Failure(_) => LnMessage(DLCOfferTLV.fromHex(offerJs.str)) - } - - val peerAddr = jsToInetSocketAddress(addrJs) - val payoutAddressJsOpt = nullToOpt(payoutAddressJs) - val payoutAddressOpt = - payoutAddressJsOpt.map(js => jsToBitcoinAddress(js)) - val changeAddressJsOpt = nullToOpt(changeAddressJs) - val changeAddressOpt = - changeAddressJsOpt.map(js => jsToBitcoinAddress(js)) - - AcceptDLC(offer, peerAddr, payoutAddressOpt, changeAddressOpt) - } - - jsArr.arr.toList match { - case offerJs :: addrJs :: Nil => - parseParameters(offerJs, addrJs, Null, Null) - case offerJs :: addrJs :: payoutAddressJs :: Nil => - parseParameters(offerJs, addrJs, payoutAddressJs, Null) - case offerJs :: addrJs :: payoutAddressJs :: changeAddressJs :: Nil => - parseParameters(offerJs, addrJs, payoutAddressJs, changeAddressJs) - case Nil => - Failure( - new IllegalArgumentException("Missing offer and peerAddr argument")) - - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 2")) - } - } -} - -case class SignDLC(accept: LnMessage[DLCAcceptTLV]) - -object SignDLC extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[SignDLC] = { - jsArr.arr.toList match { - case acceptJs :: Nil => - Try { - val accept = LnMessageFactory(DLCAcceptTLV).fromHex(acceptJs.str) - SignDLC(accept) - } - case Nil => - Failure(new IllegalArgumentException("Missing accept argument")) - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 1")) - } - } -} - -case class AddDLCSigs(sigs: LnMessage[DLCSignTLV]) - -object AddDLCSigs extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[AddDLCSigs] = { - jsArr.arr.toList match { - case sigsJs :: Nil => - Try { - val sigs = LnMessageFactory(DLCSignTLV).fromHex(sigsJs.str) - AddDLCSigs(sigs) - } - case Nil => - Failure(new IllegalArgumentException("Missing sigs argument")) - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 1")) - } - } -} - case class DLCDataFromFile( path: Path, destinationOpt: Option[Path], @@ -1151,309 +148,6 @@ object DLCDataFromFile extends ServerJsonModels { } } -case class GetDLCFundingTx(contractId: ByteVector) - -object GetDLCFundingTx extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[GetDLCFundingTx] = { - jsArr.arr.toList match { - case contractIdJs :: Nil => - Try { - val contractId = ByteVector.fromValidHex(contractIdJs.str) - GetDLCFundingTx(contractId) - } - case Nil => - Failure(new IllegalArgumentException("Missing contractId argument")) - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 1")) - } - } -} - -case class BroadcastDLCFundingTx(contractId: ByteVector) - -object BroadcastDLCFundingTx extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[BroadcastDLCFundingTx] = { - jsArr.arr.toList match { - case contractIdJs :: Nil => - Try { - val contractId = ByteVector.fromValidHex(contractIdJs.str) - BroadcastDLCFundingTx(contractId) - } - case Nil => - Failure(new IllegalArgumentException("Missing contractId argument")) - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 1")) - } - } -} - -case class ExecuteDLC( - contractId: ByteVector, - oracleSigs: Vector[OracleAttestmentTLV], - noBroadcast: Boolean) - extends Broadcastable - -object ExecuteDLC extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[ExecuteDLC] = { - jsArr.arr.toList match { - case contractIdJs :: oracleSigsJs :: noBroadcastJs :: Nil => - Try { - val contractId = ByteVector.fromValidHex(contractIdJs.str) - val oracleSigs = jsToOracleAttestmentTLVVec(oracleSigsJs) - val noBroadcast = noBroadcastJs.bool - - ExecuteDLC(contractId, oracleSigs, noBroadcast) - } - case Nil => - Failure( - new IllegalArgumentException( - "Missing contractId, oracleSigs, and noBroadcast arguments")) - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 3")) - } - } -} - -case class ExecuteDLCRefund(contractId: ByteVector, noBroadcast: Boolean) - extends Broadcastable - -object ExecuteDLCRefund extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[ExecuteDLCRefund] = { - jsArr.arr.toList match { - case contractIdJs :: noBroadcastJs :: Nil => - Try { - val contractId = ByteVector.fromValidHex(contractIdJs.str) - val noBroadcast = noBroadcastJs.bool - - ExecuteDLCRefund(contractId, noBroadcast) - } - case Nil => - Failure(new IllegalArgumentException("Missing contractId argument")) - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 2")) - } - } -} - -case class SendFromOutpoints( - outPoints: Vector[TransactionOutPoint], - address: BitcoinAddress, - amount: Bitcoins, - satoshisPerVirtualByte: Option[SatoshisPerVirtualByte]) - -object SendFromOutpoints extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[SendFromOutpoints] = { - jsArr.arr.toList match { - case outPointsJs :: addrJs :: bitcoinsJs :: satsPerVBytesJs :: Nil => - Try { - val outPoints = jsToTransactionOutPointSeq(outPointsJs).toVector - val address = jsToBitcoinAddress(addrJs) - val bitcoins = Bitcoins(bitcoinsJs.num) - val satoshisPerVirtualByte = - nullToOpt(satsPerVBytesJs).map(satsPerVBytes => - SatoshisPerVirtualByte(Satoshis(satsPerVBytes.num.toLong))) - SendFromOutpoints(outPoints, - address, - bitcoins, - satoshisPerVirtualByte) - } - case Nil => - Failure( - new IllegalArgumentException( - "Missing outPoints, address, amount, and fee rate arguments")) - - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 4")) - } - } -} - -case class SweepWallet( - address: BitcoinAddress, - satoshisPerVirtualByteOpt: Option[SatoshisPerVirtualByte]) - -object SweepWallet extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[SweepWallet] = { - jsArr.arr.toList match { - case addrJs :: satsPerVBytesJs :: Nil => - Try { - val address = jsToBitcoinAddress(addrJs) - val satoshisPerVirtualByte = - nullToOpt(satsPerVBytesJs).map(satsPerVBytes => - SatoshisPerVirtualByte(Satoshis(satsPerVBytes.num.toLong))) - SweepWallet(address, satoshisPerVirtualByte) - } - case addrJs :: Nil => - Try { - val address = jsToBitcoinAddress(addrJs) - SweepWallet(address, None) - } - case Nil => - Failure( - new IllegalArgumentException( - "Missing address and fee rate arguments")) - - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 2")) - } - } -} - -case class SendWithAlgo( - address: BitcoinAddress, - amount: Bitcoins, - satoshisPerVirtualByte: Option[SatoshisPerVirtualByte], - algo: CoinSelectionAlgo) - -object SendWithAlgo extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[SendWithAlgo] = { - jsArr.arr.toList match { - case addrJs :: bitcoinsJs :: satsPerVBytesJs :: algoJs :: Nil => - Try { - val address = jsToBitcoinAddress(addrJs) - val bitcoins = Bitcoins(bitcoinsJs.num) - val satoshisPerVirtualByte = - nullToOpt(satsPerVBytesJs).map(satsPerVBytes => - SatoshisPerVirtualByte(Satoshis(satsPerVBytes.num.toLong))) - val algo = jsToCoinSelectionAlgo(algoJs) - - SendWithAlgo(address, bitcoins, satoshisPerVirtualByte, algo) - } - case Nil => - Failure( - new IllegalArgumentException( - "Missing address, amount, fee rate, and algo arguments")) - - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 4")) - } - } - -} - -case class SignPSBT(psbt: PSBT) - -object SignPSBT extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[SignPSBT] = { - require(jsArr.arr.size == 1, - s"Bad number of arguments: ${jsArr.arr.size}. Expected: 1") - - Try(SignPSBT(jsToPSBT(jsArr.arr.head))) - } -} - -case class OpReturnCommit( - message: String, - hashMessage: Boolean, - feeRateOpt: Option[SatoshisPerVirtualByte]) - -object OpReturnCommit extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[OpReturnCommit] = { - jsArr.arr.toList match { - case messageJs :: hashMessageJs :: feeRateOptJs :: Nil => - Try { - val message = messageJs.str - val hashMessage = hashMessageJs.bool - val feeRateOpt = - nullToOpt(feeRateOptJs).map(satsPerVBytes => - SatoshisPerVirtualByte(Satoshis(satsPerVBytes.num.toLong))) - OpReturnCommit(message, hashMessage, feeRateOpt) - } - case Nil => - Failure( - new IllegalArgumentException( - "Missing message, hashMessage, and fee rate arguments")) - - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 3")) - } - } -} - -case class BumpFee(txId: DoubleSha256DigestBE, feeRate: SatoshisPerVirtualByte) - -object BumpFee extends ServerJsonModels { - - def fromJsArr(jsArr: ujson.Arr): Try[BumpFee] = { - jsArr.arr.toList match { - case txIdJs :: feeRateJs :: Nil => - Try { - val txId = DoubleSha256DigestBE(txIdJs.str) - val feeRate = SatoshisPerVirtualByte(Satoshis(feeRateJs.num.toLong)) - BumpFee(txId, feeRate) - } - case Nil => - Failure( - new IllegalArgumentException("Missing txId and fee rate arguments")) - - case other => - Failure( - new IllegalArgumentException( - s"Bad number of arguments: ${other.length}. Expected: 2")) - } - } -} - -case class CreateContractInfo( - announcementTLV: OracleAnnouncementTLV, - totalCollateral: Satoshis, - contractDescriptorTLV: ContractDescriptorTLV) { - - def contractDescriptor: ContractDescriptor = { - ContractDescriptor.fromTLV(contractDescriptorTLV) - } -} - -object CreateContractInfo extends ServerJsonModels { - - def fromJsArr(arr: ujson.Arr): Try[CreateContractInfo] = { - arr.arr.toVector match { - case announcementVal +: totalCollateralVal +: payoutsVal +: Vector() => - Try { - val announcementTLV = - OracleAnnouncementTLV.fromHex(announcementVal.str) - val totalCollateral = Satoshis(totalCollateralVal.num.toLong) - //validate that these are part of the announcement? - val contractDescriptor = - ContractDescriptorParser.parseCmdLine(payoutsVal, announcementTLV) - - CreateContractInfo(announcementTLV, - totalCollateral, - contractDescriptor) - } - case other => - val exn = new IllegalArgumentException( - s"Bad number or arguments to createcontractinfo, got=${other.length} expected=3") - Failure(exn) - } - } -} - case class OfferAdd( offerTLV: DLCOfferTLV, peer: Option[String], diff --git a/app/server/src/main/scala/org/bitcoins/server/WalletRoutes.scala b/app/server/src/main/scala/org/bitcoins/server/WalletRoutes.scala index 5df0d1b5de..efddb18b3e 100644 --- a/app/server/src/main/scala/org/bitcoins/server/WalletRoutes.scala +++ b/app/server/src/main/scala/org/bitcoins/server/WalletRoutes.scala @@ -6,6 +6,7 @@ import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server._ import akka.stream.Materializer import grizzled.slf4j.Logging +import org.bitcoins.commons.rpc._ import org.bitcoins.commons.serializers.Picklers._ import org.bitcoins.core.api.dlc.wallet.DLCNeutrinoHDWalletApi import org.bitcoins.core.api.wallet.db.SpendingInfoDb @@ -630,10 +631,9 @@ case class WalletRoutes(wallet: DLCNeutrinoHDWalletApi)(implicit } } } - case ServerCommand("sendfromoutpoints", arr) => - withValidServerCommand(SendFromOutpoints.fromJsArr(arr)) { - case SendFromOutpoints(outPoints, + withValidServerCommand(SendFromOutPoints.fromJsArr(arr)) { + case SendFromOutPoints(outPoints, address, bitcoins, satoshisPerVirtualByteOpt) =>