From d670d138501d8ed76cf149d66e0235b1cea4d568 Mon Sep 17 00:00:00 2001 From: Alexis Hernandez Date: Sat, 6 Apr 2019 22:41:39 -0700 Subject: [PATCH] server: Add getOutput method to the TransactionDataHandler --- .../data/TransactionDataHandler.scala | 2 ++ .../TransactionPostgresDataHandler.scala | 8 ++++- .../dao/TransactionOutputPostgresDAO.scala | 14 +++++++++ .../async/TransactionFutureDataHandler.scala | 4 +++ .../TransactionPostgresDataHandlerSpec.scala | 31 ++++++++++++++++++- .../helpers/TransactionDummyDataHandler.scala | 2 ++ 6 files changed, 59 insertions(+), 2 deletions(-) diff --git a/server/app/com/xsn/explorer/data/TransactionDataHandler.scala b/server/app/com/xsn/explorer/data/TransactionDataHandler.scala index a922da9..6327999 100644 --- a/server/app/com/xsn/explorer/data/TransactionDataHandler.scala +++ b/server/app/com/xsn/explorer/data/TransactionDataHandler.scala @@ -21,6 +21,8 @@ trait TransactionDataHandler[F[_]] { def getUnspentOutputs(address: Address): F[List[Transaction.Output]] + def getOutput(txid: TransactionId, index: Int): F[Transaction.Output] + def getByBlockhash( blockhash: Blockhash, paginatedQuery: PaginatedQuery, diff --git a/server/app/com/xsn/explorer/data/anorm/TransactionPostgresDataHandler.scala b/server/app/com/xsn/explorer/data/anorm/TransactionPostgresDataHandler.scala index 983408f..0109b70 100644 --- a/server/app/com/xsn/explorer/data/anorm/TransactionPostgresDataHandler.scala +++ b/server/app/com/xsn/explorer/data/anorm/TransactionPostgresDataHandler.scala @@ -5,12 +5,13 @@ import com.alexitc.playsonify.models.ordering.{FieldOrdering, OrderingCondition} import com.alexitc.playsonify.models.pagination.{Limit, PaginatedQuery, PaginatedResult} import com.xsn.explorer.data.TransactionBlockingDataHandler import com.xsn.explorer.data.anorm.dao.{TransactionOutputPostgresDAO, TransactionPostgresDAO} +import com.xsn.explorer.errors.TransactionNotFoundError import com.xsn.explorer.models._ import com.xsn.explorer.models.fields.TransactionField import com.xsn.explorer.models.persisted.Transaction import com.xsn.explorer.models.values.{Address, Blockhash, TransactionId} import javax.inject.Inject -import org.scalactic.Good +import org.scalactic.{Good, One, Or} import play.api.db.Database class TransactionPostgresDataHandler @Inject() ( @@ -50,6 +51,11 @@ class TransactionPostgresDataHandler @Inject() ( Good(result) } + override def getOutput(txid: TransactionId, index: Int): ApplicationResult[Transaction.Output] = withConnection { implicit conn => + val maybe = transactionOutputDAO.getOutput(txid, index) + Or.from(maybe, One(TransactionNotFoundError)) + } + override def getByBlockhash( blockhash: Blockhash, paginatedQuery: PaginatedQuery, diff --git a/server/app/com/xsn/explorer/data/anorm/dao/TransactionOutputPostgresDAO.scala b/server/app/com/xsn/explorer/data/anorm/dao/TransactionOutputPostgresDAO.scala index 9bb3526..da8541c 100644 --- a/server/app/com/xsn/explorer/data/anorm/dao/TransactionOutputPostgresDAO.scala +++ b/server/app/com/xsn/explorer/data/anorm/dao/TransactionOutputPostgresDAO.scala @@ -26,6 +26,20 @@ class TransactionOutputPostgresDAO { ).as(parseTransactionOutput.*) } + def getOutput(txid: TransactionId, index: Int)(implicit conn: Connection): Option[Transaction.Output] = { + SQL( + """ + |SELECT txid, index, value, address, hex_script + |FROM transaction_outputs + |WHERE txid = {txid} AND + | index = {index} + """.stripMargin + ).on( + 'txid -> txid.string, + 'index -> index + ).as(parseTransactionOutput.singleOpt) + } + def batchInsertOutputs( outputs: List[Transaction.Output])( implicit conn: Connection): Option[List[Transaction.Output]] = { diff --git a/server/app/com/xsn/explorer/data/async/TransactionFutureDataHandler.scala b/server/app/com/xsn/explorer/data/async/TransactionFutureDataHandler.scala index 52e4dbf..67488d7 100644 --- a/server/app/com/xsn/explorer/data/async/TransactionFutureDataHandler.scala +++ b/server/app/com/xsn/explorer/data/async/TransactionFutureDataHandler.scala @@ -39,6 +39,10 @@ class TransactionFutureDataHandler @Inject() ( blockingDataHandler.getUnspentOutputs(address) } + override def getOutput(txid: TransactionId, index: Int): FutureApplicationResult[Transaction.Output] = Future { + blockingDataHandler.getOutput(txid, index) + } + override def getByBlockhash( blockhash: Blockhash, paginatedQuery: PaginatedQuery, diff --git a/server/test/com/xsn/explorer/data/TransactionPostgresDataHandlerSpec.scala b/server/test/com/xsn/explorer/data/TransactionPostgresDataHandlerSpec.scala index 1192101..e72f859 100644 --- a/server/test/com/xsn/explorer/data/TransactionPostgresDataHandlerSpec.scala +++ b/server/test/com/xsn/explorer/data/TransactionPostgresDataHandlerSpec.scala @@ -13,7 +13,7 @@ import com.xsn.explorer.models.fields.TransactionField import com.xsn.explorer.models.persisted.Transaction import com.xsn.explorer.models.rpc.Block import com.xsn.explorer.models.values.{Height, _} -import org.scalactic.{Good, One, Or} +import org.scalactic.{Bad, Good, One, Or} import org.scalatest.BeforeAndAfter class TransactionPostgresDataHandlerSpec extends PostgresDataHandlerSpec with BeforeAndAfter { @@ -89,6 +89,35 @@ class TransactionPostgresDataHandlerSpec extends PostgresDataHandlerSpec with Be } } + "getOutput" should { + val txid = createTransactionId("67aa0bd8b9297ca6ee25a1e5c2e3a8dbbcc1e20eab76b6d1bdf9d69f8a5356b8") + + "return the output" in { + val index = 0 + val expected = Transaction.Output( + address = createAddress("XdJnCKYNwzCz8ATv8Eu75gonaHyfr9qXg9"), + txid = createTransactionId("67aa0bd8b9297ca6ee25a1e5c2e3a8dbbcc1e20eab76b6d1bdf9d69f8a5356b8"), + index = 0, + value = BigDecimal(76500000.000000000000000), + script = HexString.from("2103e8c52f2c5155771492907095753a43ce776e1fa7c5e769a67a9f3db4467ec029ac").get + ) + + clearDatabase() + val blocks = blockList.take(3) + blocks.map(createBlock) + + val result = dataHandler.getOutput(txid, index) + result must be(Good(expected)) + } + + "return no value" in { + val index = 10 + + val result = dataHandler.getOutput(txid, index) + result must be(Bad(TransactionNotFoundError).accumulating) + } + } + "getByBlockhash" should { val blockhash = randomBlockhash diff --git a/server/test/com/xsn/explorer/helpers/TransactionDummyDataHandler.scala b/server/test/com/xsn/explorer/helpers/TransactionDummyDataHandler.scala index fe95f17..af4db35 100644 --- a/server/test/com/xsn/explorer/helpers/TransactionDummyDataHandler.scala +++ b/server/test/com/xsn/explorer/helpers/TransactionDummyDataHandler.scala @@ -18,6 +18,8 @@ class TransactionDummyDataHandler extends TransactionBlockingDataHandler { override def getUnspentOutputs(address: Address): ApplicationResult[List[Transaction.Output]] = ??? + override def getOutput(txid: TransactionId, index: Int): ApplicationResult[Transaction.Output] = ??? + override def getByBlockhash(blockhash: Blockhash, paginatedQuery: PaginatedQuery, ordering: FieldOrdering[TransactionField]): ApplicationResult[PaginatedResult[TransactionWithValues]] = ??? override def getByBlockhash(blockhash: Blockhash, limit: pagination.Limit, lastSeenTxid: Option[TransactionId]): ApplicationResult[List[TransactionWithValues]] = ???