diff --git a/.gitignore b/.gitignore index cd2e98e6b8..6687ed7a96 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ project/plugins/project/ .idea/* *.iml .bsp +.vscode # secp256k1 ignore files bench_inv diff --git a/app/server-routes/src/main/scala/org/bitcoins/server/routes/Server.scala b/app/server-routes/src/main/scala/org/bitcoins/server/routes/Server.scala index fbb7ad4ee1..99cf101f19 100644 --- a/app/server-routes/src/main/scala/org/bitcoins/server/routes/Server.scala +++ b/app/server-routes/src/main/scala/org/bitcoins/server/routes/Server.scala @@ -67,7 +67,7 @@ case class Server( pathSingleSlash { post { entity(as[ServerCommand]) { cmd => - val init = PartialFunction.empty[ServerCommand, StandardRoute] + val init = PartialFunction.empty[ServerCommand, Route] val handler = handlers.foldLeft(init) { case (accum, curr) => accum.orElse(curr.handleCommand) } diff --git a/app/server-routes/src/main/scala/org/bitcoins/server/routes/ServerRoute.scala b/app/server-routes/src/main/scala/org/bitcoins/server/routes/ServerRoute.scala index 2fa1f7b765..3919e6446f 100644 --- a/app/server-routes/src/main/scala/org/bitcoins/server/routes/ServerRoute.scala +++ b/app/server-routes/src/main/scala/org/bitcoins/server/routes/ServerRoute.scala @@ -1,7 +1,18 @@ package org.bitcoins.server.routes -import akka.http.scaladsl.server.StandardRoute +import akka.http.scaladsl.server.Route +import akka.http.scaladsl.server.Directives._ +import akka.http.scaladsl.server.Directive1 +import akka.http.scaladsl.server.ValidationRejection + +import scala.util.Try trait ServerRoute { - def handleCommand: PartialFunction[ServerCommand, StandardRoute] + def handleCommand: PartialFunction[ServerCommand, Route] + + def withValidServerCommand[R](validator: Try[R]): Directive1[R] = + validator.fold( + e => reject(ValidationRejection("failure", Some(e))), + provide + ) } 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 2399d5f36d..0ac0bb294a 100644 --- a/app/server/src/main/scala/org/bitcoins/server/ChainRoutes.scala +++ b/app/server/src/main/scala/org/bitcoins/server/ChainRoutes.scala @@ -12,14 +12,13 @@ import scodec.bits.ByteVector import ujson._ import scala.concurrent.Future -import scala.util.{Failure, Success} case class ChainRoutes(chain: ChainApi, network: BitcoinNetwork)(implicit system: ActorSystem) extends ServerRoute { import system.dispatcher - def handleCommand: PartialFunction[ServerCommand, StandardRoute] = { + def handleCommand: PartialFunction[ServerCommand, Route] = { case ServerCommand("getblockcount", _) => complete { chain.getBlockCount().map { count => @@ -46,10 +45,8 @@ case class ChainRoutes(chain: ChainApi, network: BitcoinNetwork)(implicit } case ServerCommand("getblockheader", arr) => - GetBlockHeader.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success(GetBlockHeader(hash)) => + withValidServerCommand(GetBlockHeader.fromJsArr(arr)) { + case GetBlockHeader(hash) => complete { chain.getHeader(hash).flatMap { case None => Future.successful(Server.httpSuccess(ujson.Null)) 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 0d3e4d4229..6d53faaeef 100644 --- a/app/server/src/main/scala/org/bitcoins/server/CoreRoutes.scala +++ b/app/server/src/main/scala/org/bitcoins/server/CoreRoutes.scala @@ -17,7 +17,6 @@ import org.bitcoins.server.routes.{Server, ServerCommand, ServerRoute} import ujson._ import scala.collection.mutable -import scala.util.{Failure, Success} case class CoreRoutes(core: CoreApi)(implicit system: ActorSystem, @@ -25,72 +24,60 @@ case class CoreRoutes(core: CoreApi)(implicit extends ServerRoute { import system.dispatcher - def handleCommand: PartialFunction[ServerCommand, StandardRoute] = { + def handleCommand: PartialFunction[ServerCommand, Route] = { case ServerCommand("finalizepsbt", arr) => - FinalizePSBT.fromJsArr(arr) match { - case Success(FinalizePSBT(psbt)) => + withValidServerCommand(FinalizePSBT.fromJsArr(arr)) { + case FinalizePSBT(psbt) => complete { core .finalizePSBT(psbt) .map(finalized => Server.httpSuccess(finalized.base64)) } - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) } case ServerCommand("extractfrompsbt", arr) => - ExtractFromPSBT.fromJsArr(arr) match { - case Success(ExtractFromPSBT(psbt)) => + withValidServerCommand(ExtractFromPSBT.fromJsArr(arr)) { + case ExtractFromPSBT(psbt) => complete { core .extractFromPSBT(psbt) .map(tx => Server.httpSuccess(tx.hex)) } - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) } case ServerCommand("converttopsbt", arr) => - ConvertToPSBT.fromJsArr(arr) match { - case Success(ConvertToPSBT(tx)) => + withValidServerCommand(ConvertToPSBT.fromJsArr(arr)) { + case ConvertToPSBT(tx) => complete { core .convertToPSBT(tx) .map(psbt => Server.httpSuccess(psbt.base64)) } - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) } case ServerCommand("combinepsbts", arr) => - CombinePSBTs.fromJsArr(arr) match { - case Success(CombinePSBTs(psbts)) => + withValidServerCommand(CombinePSBTs.fromJsArr(arr)) { + case CombinePSBTs(psbts) => complete { core .combinePSBTs(psbts) .map(psbt => Server.httpSuccess(psbt.base64)) } - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) } case ServerCommand("joinpsbts", arr) => - JoinPSBTs.fromJsArr(arr) match { - case Success(JoinPSBTs(psbts)) => + withValidServerCommand(JoinPSBTs.fromJsArr(arr)) { + case JoinPSBTs(psbts) => complete { core .joinPSBTs(psbts) .map(psbt => Server.httpSuccess(psbt.base64)) } - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) } case ServerCommand("decoderawtransaction", arr) => - DecodeRawTransaction.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success(DecodeRawTransaction(tx)) => + withValidServerCommand(DecodeRawTransaction.fromJsArr(arr)) { + case DecodeRawTransaction(tx) => complete { val decoded = SerializedTransaction.decodeRawTransaction(tx) val uJson = ujson.read(decoded.toJson.toString()) @@ -99,10 +86,8 @@ case class CoreRoutes(core: CoreApi)(implicit } case ServerCommand("decodepsbt", arr) => - DecodePSBT.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success(DecodePSBT(psbt)) => + withValidServerCommand(DecodePSBT.fromJsArr(arr)) { + case DecodePSBT(psbt) => complete { val decoded = SerializedPSBT.decodePSBT(psbt) val uJson = ujson.read(decoded.toJson.toString()) @@ -111,10 +96,8 @@ case class CoreRoutes(core: CoreApi)(implicit } case ServerCommand("analyzepsbt", arr) => - AnalyzePSBT.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success(AnalyzePSBT(psbt)) => + withValidServerCommand(AnalyzePSBT.fromJsArr(arr)) { + case AnalyzePSBT(psbt) => complete { val inputs = psbt.inputMaps.zipWithIndex.map { case (inputMap, index) => @@ -168,10 +151,8 @@ case class CoreRoutes(core: CoreApi)(implicit } case ServerCommand("createmultisig", arr) => - CreateMultisig.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success(CreateMultisig(requiredKeys, keys, addressType)) => + withValidServerCommand(CreateMultisig.fromJsArr(arr)) { + case CreateMultisig(requiredKeys, keys, addressType) => complete { val sorted = keys.sortBy(_.hex) val spk = MultiSignatureScriptPubKey(requiredKeys, sorted) diff --git a/app/server/src/main/scala/org/bitcoins/server/NodeRoutes.scala b/app/server/src/main/scala/org/bitcoins/server/NodeRoutes.scala index ddb224228e..67fe178b31 100644 --- a/app/server/src/main/scala/org/bitcoins/server/NodeRoutes.scala +++ b/app/server/src/main/scala/org/bitcoins/server/NodeRoutes.scala @@ -10,13 +10,12 @@ import org.bitcoins.rpc.client.common.BitcoindRpcClient import org.bitcoins.server.routes.{Server, ServerCommand, ServerRoute} import scala.concurrent.duration.DurationInt -import scala.util.{Failure, Success} case class NodeRoutes(nodeApi: NodeApi)(implicit system: ActorSystem) extends ServerRoute { import system.dispatcher - def handleCommand: PartialFunction[ServerCommand, StandardRoute] = { + def handleCommand: PartialFunction[ServerCommand, Route] = { case ServerCommand("getpeers", _) => complete { Server.httpSuccess("TODO implement getpeers") @@ -45,10 +44,8 @@ case class NodeRoutes(nodeApi: NodeApi)(implicit system: ActorSystem) } case ServerCommand("sendrawtransaction", arr) => - SendRawTransaction.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success(SendRawTransaction(tx)) => + withValidServerCommand(SendRawTransaction.fromJsArr(arr)) { + case SendRawTransaction(tx) => complete { nodeApi.broadcastTransaction(tx).map { _ => Server.httpSuccess(tx.txIdBE) 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 6f437791fb..dc21de17a6 100644 --- a/app/server/src/main/scala/org/bitcoins/server/WalletRoutes.scala +++ b/app/server/src/main/scala/org/bitcoins/server/WalletRoutes.scala @@ -18,7 +18,6 @@ import ujson._ import java.time.Instant import scala.concurrent.Future -import scala.util.{Failure, Success} case class WalletRoutes(wallet: AnyHDWalletApi)(implicit system: ActorSystem, @@ -45,7 +44,7 @@ case class WalletRoutes(wallet: AnyHDWalletApi)(implicit } } - def handleCommand: PartialFunction[ServerCommand, StandardRoute] = { + def handleCommand: PartialFunction[ServerCommand, Route] = { case ServerCommand("isempty", _) => complete { @@ -62,64 +61,47 @@ case class WalletRoutes(wallet: AnyHDWalletApi)(implicit } case ServerCommand("getbalance", arr) => - GetBalance.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success(GetBalance(isSats)) => + withValidServerCommand(GetBalance.fromJsArr(arr)) { + case GetBalance(isSats) => complete { wallet.getBalance().map { balance => Server.httpSuccess( - if (isSats) { - balance.satoshis.toString - } else { - Bitcoins(balance.satoshis).toString - } + if (isSats) balance.satoshis.toString + else Bitcoins(balance.satoshis).toString ) } } } case ServerCommand("getconfirmedbalance", arr) => - GetBalance.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success(GetBalance(isSats)) => + withValidServerCommand(GetBalance.fromJsArr(arr)) { + case GetBalance(isSats) => complete { wallet.getConfirmedBalance().map { balance => Server.httpSuccess( - if (isSats) { - balance.satoshis.toString - } else { - Bitcoins(balance.satoshis).toString - } + if (isSats) balance.satoshis.toString + else Bitcoins(balance.satoshis).toString ) } } } case ServerCommand("getunconfirmedbalance", arr) => - GetBalance.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success(GetBalance(isSats)) => + withValidServerCommand(GetBalance.fromJsArr(arr)) { + case GetBalance(isSats) => complete { wallet.getUnconfirmedBalance().map { balance => Server.httpSuccess( - if (isSats) { - balance.satoshis.toString - } else { - Bitcoins(balance.satoshis).toString - } + if (isSats) balance.satoshis.toString + else Bitcoins(balance.satoshis).toString ) } } } case ServerCommand("getnewaddress", arr) => - GetNewAddress.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success(GetNewAddress(labelOpt)) => + withValidServerCommand(GetNewAddress.fromJsArr(arr)) { + case GetNewAddress(labelOpt) => complete { val labelVec = Vector(labelOpt).flatten wallet.getNewAddress(labelVec).map { address => @@ -129,10 +111,8 @@ case class WalletRoutes(wallet: AnyHDWalletApi)(implicit } case ServerCommand("gettransaction", arr) => - GetTransaction.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success(GetTransaction(txId)) => + withValidServerCommand(GetTransaction.fromJsArr(arr)) { + case GetTransaction(txId) => complete { wallet.findTransaction(txId).map { case None => @@ -144,10 +124,8 @@ case class WalletRoutes(wallet: AnyHDWalletApi)(implicit } case ServerCommand("lockunspent", arr) => - LockUnspent.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success(LockUnspent(unlock, outputParams)) => + withValidServerCommand(LockUnspent.fromJsArr(arr)) { + case LockUnspent(unlock, outputParams) => complete { val func: Vector[SpendingInfoDb] => Future[Vector[SpendingInfoDb]] = utxos => { @@ -173,10 +151,8 @@ case class WalletRoutes(wallet: AnyHDWalletApi)(implicit } case ServerCommand("labeladdress", arr) => - LabelAddress.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success(LabelAddress(address, label)) => + withValidServerCommand(LabelAddress.fromJsArr(arr)) { + case LabelAddress(address, label) => complete { wallet.tagAddress(address, label).map { tagDb => Server.httpSuccess( @@ -186,10 +162,8 @@ case class WalletRoutes(wallet: AnyHDWalletApi)(implicit } case ServerCommand("getaddresstags", arr) => - GetAddressTags.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success(GetAddressTags(address)) => + withValidServerCommand(GetAddressTags.fromJsArr(arr)) { + case GetAddressTags(address) => complete { wallet.getAddressTags(address).map { tagDbs => val retStr = tagDbs.map(_.tagName.name) @@ -199,10 +173,8 @@ case class WalletRoutes(wallet: AnyHDWalletApi)(implicit } case ServerCommand("getaddresslabels", arr) => - GetAddressLabels.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success(GetAddressLabels(address)) => + withValidServerCommand(GetAddressLabels.fromJsArr(arr)) { + case GetAddressLabels(address) => complete { wallet.getAddressTags(address, AddressLabelTagType).map { tagDbs => val retStr = tagDbs.map(_.tagName.name) @@ -212,10 +184,8 @@ case class WalletRoutes(wallet: AnyHDWalletApi)(implicit } case ServerCommand("dropaddresslabels", arr) => - DropAddressLabels.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success(DropAddressLabels(address)) => + withValidServerCommand(DropAddressLabels.fromJsArr(arr)) { + case DropAddressLabels(address) => complete { wallet.dropAddressTagType(address, AddressLabelTagType).map { numDropped => @@ -231,15 +201,11 @@ case class WalletRoutes(wallet: AnyHDWalletApi)(implicit } case ServerCommand("sendtoaddress", arr) => - // TODO create custom directive for this? - SendToAddress.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success( - SendToAddress(address, - bitcoins, - satoshisPerVirtualByteOpt, - noBroadcast)) => + withValidServerCommand(SendToAddress.fromJsArr(arr)) { + case SendToAddress(address, + bitcoins, + satoshisPerVirtualByteOpt, + noBroadcast) => complete { for { tx <- wallet.sendToAddress(address, @@ -253,14 +219,11 @@ case class WalletRoutes(wallet: AnyHDWalletApi)(implicit } case ServerCommand("sendfromoutpoints", arr) => - SendFromOutpoints.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success( - SendFromOutpoints(outPoints, - address, - bitcoins, - satoshisPerVirtualByteOpt)) => + withValidServerCommand(SendFromOutpoints.fromJsArr(arr)) { + case SendFromOutpoints(outPoints, + address, + bitcoins, + satoshisPerVirtualByteOpt) => complete { for { tx <- wallet.sendFromOutPoints(outPoints, @@ -273,14 +236,8 @@ case class WalletRoutes(wallet: AnyHDWalletApi)(implicit } case ServerCommand("sendwithalgo", arr) => - SendWithAlgo.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success( - SendWithAlgo(address, - bitcoins, - satoshisPerVirtualByteOpt, - algo)) => + withValidServerCommand(SendWithAlgo.fromJsArr(arr)) { + case SendWithAlgo(address, bitcoins, satoshisPerVirtualByteOpt, algo) => complete { for { tx <- wallet.sendWithAlgo(address, @@ -293,25 +250,17 @@ case class WalletRoutes(wallet: AnyHDWalletApi)(implicit } case ServerCommand("signpsbt", arr) => - SignPSBT.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success(SignPSBT(psbt)) => - complete { - wallet.signPSBT(psbt).map { signed => - Server.httpSuccess(signed.base64) - } + withValidServerCommand(SignPSBT.fromJsArr(arr)) { case SignPSBT(psbt) => + complete { + wallet.signPSBT(psbt).map { signed => + Server.httpSuccess(signed.base64) } + } } case ServerCommand("opreturncommit", arr) => - OpReturnCommit.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success( - OpReturnCommit(message, - hashMessage, - satoshisPerVirtualByteOpt)) => + withValidServerCommand(OpReturnCommit.fromJsArr(arr)) { + case OpReturnCommit(message, hashMessage, satoshisPerVirtualByteOpt) => complete { for { tx <- wallet.makeOpReturnCommitment(message, @@ -325,10 +274,8 @@ case class WalletRoutes(wallet: AnyHDWalletApi)(implicit } case ServerCommand("bumpfeerbf", arr) => - BumpFee.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success(BumpFee(txId, feeRate)) => + withValidServerCommand(BumpFee.fromJsArr(arr)) { + case BumpFee(txId, feeRate) => complete { for { tx <- wallet.bumpFeeRBF(txId, feeRate) @@ -338,10 +285,8 @@ case class WalletRoutes(wallet: AnyHDWalletApi)(implicit } case ServerCommand("bumpfeecpfp", arr) => - BumpFee.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success(BumpFee(txId, feeRate)) => + withValidServerCommand(BumpFee.fromJsArr(arr)) { + case BumpFee(txId, feeRate) => complete { for { tx <- wallet.bumpFeeCPFP(txId, feeRate) @@ -351,15 +296,12 @@ case class WalletRoutes(wallet: AnyHDWalletApi)(implicit } case ServerCommand("rescan", arr) => - Rescan.fromJsArr(arr) match { - case Failure(exception) => - reject(ValidationRejection("failure", Some(exception))) - case Success( - Rescan(batchSize, - startBlock, - endBlock, - force, - ignoreCreationTime)) => + withValidServerCommand(Rescan.fromJsArr(arr)) { + case Rescan(batchSize, + startBlock, + endBlock, + force, + ignoreCreationTime) => complete { val res = for { empty <- wallet.isEmpty() @@ -448,10 +390,8 @@ case class WalletRoutes(wallet: AnyHDWalletApi)(implicit } case ServerCommand("getaddressinfo", arr) => - GetAddressInfo.fromJsArr(arr) match { - case Failure(err) => - reject(ValidationRejection("failure", Some(err))) - case Success(GetAddressInfo(address)) => + withValidServerCommand(GetAddressInfo.fromJsArr(arr)) { + case GetAddressInfo(address) => complete { wallet.getAddressInfo(address).map { case Some(addressInfo) => @@ -478,10 +418,8 @@ case class WalletRoutes(wallet: AnyHDWalletApi)(implicit } case ServerCommand("keymanagerpassphrasechange", arr) => - KeyManagerPassphraseChange.fromJsArr(arr) match { - case Failure(err) => - reject(ValidationRejection("failure", Some(err))) - case Success(KeyManagerPassphraseChange(oldPassword, newPassword)) => + withValidServerCommand(KeyManagerPassphraseChange.fromJsArr(arr)) { + case KeyManagerPassphraseChange(oldPassword, newPassword) => complete { val path = walletConf.seedPath WalletStorage.changeAesPassword(path, @@ -493,10 +431,8 @@ case class WalletRoutes(wallet: AnyHDWalletApi)(implicit } case ServerCommand("keymanagerpassphraseset", arr) => - KeyManagerPassphraseSet.fromJsArr(arr) match { - case Failure(err) => - reject(ValidationRejection("failure", Some(err))) - case Success(KeyManagerPassphraseSet(password)) => + withValidServerCommand(KeyManagerPassphraseSet.fromJsArr(arr)) { + case KeyManagerPassphraseSet(password) => complete { val path = walletConf.seedPath WalletStorage.changeAesPassword(path, None, Some(password)) @@ -506,10 +442,8 @@ case class WalletRoutes(wallet: AnyHDWalletApi)(implicit } case ServerCommand("importseed", arr) => - ImportSeed.fromJsArr(arr) match { - case Failure(err) => - reject(ValidationRejection("failure", Some(err))) - case Success(ImportSeed(walletName, mnemonic, passwordOpt)) => + withValidServerCommand(ImportSeed.fromJsArr(arr)) { + case ImportSeed(walletName, mnemonic, passwordOpt) => complete { val seedPath = kmConf.seedFolder.resolve( s"$walletName-${WalletStorage.ENCRYPTED_SEED_FILE_NAME}") @@ -530,10 +464,8 @@ case class WalletRoutes(wallet: AnyHDWalletApi)(implicit } case ServerCommand("importxprv", arr) => - ImportXprv.fromJsArr(arr) match { - case Failure(err) => - reject(ValidationRejection("failure", Some(err))) - case Success(ImportXprv(walletName, xprv, passwordOpt)) => + withValidServerCommand(ImportXprv.fromJsArr(arr)) { + case ImportXprv(walletName, xprv, passwordOpt) => complete { val seedPath = kmConf.seedFolder.resolve( s"$walletName-${WalletStorage.ENCRYPTED_SEED_FILE_NAME}")