Skip to content

Commit

Permalink
server: Add GET /transactions/:txid/utxos/:index endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonathan committed Sep 19, 2019
1 parent bccebaa commit 50759b2
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 1 deletion.
20 changes: 19 additions & 1 deletion server/app/com/xsn/explorer/services/TransactionRPCService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import com.xsn.explorer.models.TransactionDetails
import com.xsn.explorer.models.rpc.Block
import com.xsn.explorer.models.values._
import com.xsn.explorer.services.validators.TransactionIdValidator
import com.xsn.explorer.util.Extensions.BigDecimalExt
import javax.inject.Inject
import org.scalactic.{One, Or}
import org.slf4j.LoggerFactory
import play.api.libs.json.{JsString, JsValue, Json}
import play.api.libs.json._

import scala.concurrent.ExecutionContext

Expand Down Expand Up @@ -73,4 +74,21 @@ class TransactionRPCService @Inject()(

result.toFuture
}

def getTransactionUtxoByIndex(
txidString: String,
index: Int,
includeMempool: Boolean
): FutureApplicationResult[JsValue] = {
val result = for {
txid <- transactionIdValidator.validate(txidString).toFutureOr
jsvalue <- xsnService.getTxOut(txid, index, includeMempool).toFutureOr
value <- Or.from((jsvalue \ "value").asOpt[BigDecimal], One(TransactionError.NotFound(txid))).toFutureOr
jsonScriptHex <- Or
.from((jsvalue \ "scriptPubKey" \ "hex").asOpt[String], One(TransactionError.NotFound(txid)))
.toFutureOr
} yield Json.obj("value" -> value.toSatoshis.toString, "script" -> jsonScriptHex)

result.toFuture
}
}
34 changes: 34 additions & 0 deletions server/app/com/xsn/explorer/services/XSNService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ trait XSNService {

def estimateSmartFee(confirmationsTarget: Int): FutureApplicationResult[JsValue]

def getTxOut(txid: TransactionId, index: Int, includeMempool: Boolean): FutureApplicationResult[JsValue]

def cleanGenesisBlock(block: rpc.Block.Canonical): rpc.Block.Canonical = {
Option(block)
.filter(_.hash == genesisBlockhash)
Expand Down Expand Up @@ -637,6 +639,38 @@ class XSNServiceRPCImpl @Inject()(
result
}

override def getTxOut(txid: TransactionId, index: Int, includeMempool: Boolean): FutureApplicationResult[JsValue] = {
val body = s"""
|{
| "jsonrpc": "1.0",
| "method": "gettxout",
| "params": ["${txid.string}", $index, $includeMempool]
|}
|""".stripMargin

val result = retrying {
server
.post(body)
.map { response =>
val maybe = getResult[JsValue](response)
maybe.getOrElse {
logger.debug(
s"Unexpected response from XSN Server, status = ${response.status}, response = ${response.body}"
)

Bad(XSNUnexpectedResponseError).accumulating
}
}
}

result.foreach {
case Bad(errors) => logger.warn(s"Failed to get TxOut, errors = $errors")
case _ => ()
}

result
}

private def mapError(json: JsValue, errorCodeMapper: Map[Int, ApplicationError]): Option[ApplicationError] = {
val jsonErrorMaybe = (json \ "error")
.asOpt[JsValue]
Expand Down
4 changes: 4 additions & 0 deletions server/app/controllers/TransactionsController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,8 @@ class TransactionsController @Inject()(transactionRPCService: TransactionRPCServ
}
.toFuture
}

def getTransactionUtxoByIndex(txid: String, index: Int, includeMempool: Boolean) = public { _ =>
transactionRPCService.getTransactionUtxoByIndex(txid, index, includeMempool)
}
}
1 change: 1 addition & 0 deletions server/conf/routes
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ GET /transactions/:txid controllers.TransactionsController.getTransacti
GET /transactions/:txid/raw controllers.TransactionsController.getRawTransaction(txid: String)
POST /transactions controllers.TransactionsController.sendRawTransaction()
GET /transactions/:txid/lite controllers.TransactionsController.getTransactionLite(txid: String)
GET /transactions/:txid/utxos/:index controllers.TransactionsController.getTransactionUtxoByIndex(txid: String, index: Int, includeMempool: Boolean ?= true)

GET /addresses/:address controllers.AddressesController.getBy(address: String)
GET /addresses/:address/tposcontracts controllers.AddressesController.getTPoSContracts(address: String)
Expand Down
2 changes: 2 additions & 0 deletions server/test/com/xsn/explorer/helpers/DummyXSNService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,6 @@ class DummyXSNService extends XSNService {
override def sendRawTransaction(hex: HexString): FutureApplicationResult[String] = ???
override def isTPoSContract(txid: TransactionId): FutureApplicationResult[Boolean] = ???
override def estimateSmartFee(confirmationsTarget: Int): FutureApplicationResult[JsValue] = ???
override def getTxOut(txid: TransactionId, index: Int, includeMempool: Boolean): FutureApplicationResult[JsValue] =
???
}

0 comments on commit 50759b2

Please sign in to comment.