Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transactions routes spec #108

Merged
merged 2 commits into from
Dec 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/main/resources/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@ paths:
default: 0
# todo from timestamp?
get:
summary: Get current of the unconfirmed transactions pool
summary: Get current pool of the unconfirmed transactions pool
operationId: getUnconfirmedTransactions
tags:
- transactions
Expand Down Expand Up @@ -709,7 +709,7 @@ paths:

/peers/connect:
post:
summary: Get current size of the unconfirmed transactions pool
summary: Add address to peers list
operationId: connectToPeer
tags:
- peers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,10 @@ import io.circe.syntax._
import org.ergoplatform.modifiers.mempool.AnyoneCanSpendTransaction
import org.ergoplatform.modifiers.mempool.proposition.AnyoneCanSpendProposition
import org.ergoplatform.nodeView.ErgoReadersHolder.{GetReaders, Readers}
import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoHistoryReader}
import org.ergoplatform.nodeView.mempool.{ErgoMemPool, ErgoMemPoolReader}
import org.ergoplatform.nodeView.state.{DigestState, UtxoState}
import org.ergoplatform.nodeView.wallet.ErgoWallet
import org.ergoplatform.nodeView.history.ErgoHistoryReader
import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader
import scorex.core.LocalInterface.LocallyGeneratedTransaction
import scorex.core.ModifierId
import scorex.core.NodeViewHolder.GetDataFromCurrentView
import scorex.core.settings.RESTApiSettings
import scorex.crypto.encode.Base16

Expand All @@ -27,23 +24,11 @@ case class TransactionsApiRoute(readersHolder: ActorRef, nodeViewActorRef: Actor
(implicit val context: ActorRefFactory) extends ErgoBaseApiRoute with FailFastCirceSupport {

override val route: Route = pathPrefix("transactions") {
concat(getUnconfirmedTransactionsR, sendTransactionR, getTransactionByIdR)
getUnconfirmedTransactionsR ~ sendTransactionR ~getTransactionByIdR
}

override val settings: RESTApiSettings = restApiSettings

private val historyRequest = if (digest) {
GetDataFromCurrentView[ErgoHistory, DigestState, ErgoWallet, ErgoMemPool, ErgoHistory](_.history)
} else {
GetDataFromCurrentView[ErgoHistory, UtxoState, ErgoWallet, ErgoMemPool, ErgoHistory](_.history)
}

private val poolRequest = if (digest) {
GetDataFromCurrentView[ErgoHistory, DigestState, ErgoWallet, ErgoMemPool, ErgoMemPool](_.pool)
} else {
GetDataFromCurrentView[ErgoHistory, UtxoState, ErgoWallet, ErgoMemPool, ErgoMemPool](_.pool)
}

private def getHistory: Future[ErgoHistoryReader] = (readersHolder ? GetReaders).mapTo[Readers].map(_.h.get)

private def getMemPool: Future[ErgoMemPoolReader] = (readersHolder ? GetReaders).mapTo[Readers].map(_.m.get)
Expand All @@ -59,33 +44,20 @@ case class TransactionsApiRoute(readersHolder: ActorRef, nodeViewActorRef: Actor
_.take(limit).toSeq
}.map(_.map(_.json).asJson)

def sendTransactionR: Route =
post {
entity(as[AnyoneCanSpendTransaction]) { tx =>
complete {
// todo validation?
nodeViewActorRef ! LocallyGeneratedTransaction[AnyoneCanSpendProposition.type, AnyoneCanSpendTransaction](tx)
StatusCodes.OK
}
}
}
//todo There in no codec for "AnyoneCanSpendTransaction" need to make one.
def sendTransactionR: Route = (post & entity(as[AnyoneCanSpendTransaction])) { tx =>
// todo validation?
nodeViewActorRef ! LocallyGeneratedTransaction[AnyoneCanSpendProposition.type, AnyoneCanSpendTransaction](tx)
complete(StatusCodes.OK)
}

// todo tx id validation
def getTransactionByIdR: Route = path(Segment) { id =>
get {
toJsonOptionalResponse {
getTransactionById(ModifierId @@ Base16.decode(id))
}
}
def getTransactionByIdR: Route = (path(Segment) & get) { id =>
getTransactionById(ModifierId @@ Base16.decode(id)).okJson()
}

def getUnconfirmedTransactionsR: Route = path("unconfirmed") {
get {
parameters('limit.as[Int] ? 50, 'offset.as[Int] ? 0) {
case (limit, offset) =>
// todo offset
toJsonResponse(getUnconfirmedTransactions(limit))
}
}
}
def getUnconfirmedTransactionsR: Route = (path("unconfirmed") & get & paging) { (offset, limit) =>
// todo offset
getUnconfirmedTransactions(limit).okJson()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ class BlocksApiRouteSpec extends FlatSpec
}
}

ignore should "post block correclty" in {
//TODO wait until method will be implemented on api
Post(prefix) ~> route ~> check {
???
}
}

it should "get last headers" in {
Get(prefix + "/lastHeaders/1") ~> route ~> check {
status shouldBe StatusCodes.OK
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class InfoRoutesSpec extends FlatSpec
it should "return info" in {
Get("/info") ~> route ~> check {
status shouldBe StatusCodes.OK
InfoRoute.makeInfoJson(nodeId, minerInfo, 0, readers, "digest").toString shouldEqual responseAs[String]
InfoRoute.makeInfoJson(nodeId, minerInfo, connectedPeers.length, readers, "digest").toString shouldEqual responseAs[String]
}
}
}
70 changes: 70 additions & 0 deletions src/test/scala/org/ergoplatform/api/routes/PeersApiRouteSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package org.ergoplatform.api.routes

import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest}
import akka.testkit.TestDuration
import io.circe.syntax._
import org.scalatest.{FlatSpec, Matchers}
import scorex.core.api.http.PeersApiRoute
import scorex.core.settings.RESTApiSettings

import scala.concurrent.duration._

//TODO move this tests to scorex
class PeersApiRouteSpec extends FlatSpec
with Matchers
with ScalatestRouteTest
with Stubs {

implicit val timeout = RouteTestTimeout(15.seconds dilated)

val restApiSettings = RESTApiSettings("localhost", 8080, None, false, 10 seconds)
val prefix = "/peers"
val routes = PeersApiRoute(pmRef, networkControllerRef, restApiSettings).route

val peersResp = peers.map { case (address, peerInfo) =>
Seq(
Some("address" -> address.toString.asJson),
Some("lastSeen" -> peerInfo.lastSeen.asJson),
peerInfo.nodeName.map(name => "name" -> name.asJson),
peerInfo.nonce.map(nonce => "nonce" -> nonce.asJson)).flatten.toMap
}.asJson.toString

val connectedPeersResp = connectedPeers.map { handshake =>
Map(
"address" -> handshake.declaredAddress.toString.asJson,
"name" -> handshake.nodeName.asJson,
"nonce" -> handshake.nodeNonce.asJson,
"lastSeen" -> handshake.time.asJson
).asJson
}.asJson

it should "get all peers" in {
Get(prefix + "/all") ~> routes ~> check {
status shouldBe StatusCodes.OK
peersResp shouldBe responseAs[String]
}
}

//can't check it cause original node using now() timestamp for last seen field
ignore should "get connected peers" in {
Get(prefix + "/connected") ~> routes ~> check {
status shouldBe StatusCodes.OK
connectedPeersResp shouldBe responseAs[String]
}
}

it should "connect to peer" in {
Post(prefix + "/connect", "localhost:8080") ~> routes ~> check {
status shouldBe StatusCodes.OK
}
}

it should "get blacklisted peers" in {
Get(prefix + "/blacklisted") ~> routes ~> check {
status shouldBe StatusCodes.OK
println(responseAs[String])
Map("address" -> blacklistedPeers).asJson.toString shouldBe responseAs[String]
}
}
}
53 changes: 50 additions & 3 deletions src/test/scala/org/ergoplatform/api/routes/Stubs.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.ergoplatform.api.routes

import java.net.InetSocketAddress

import akka.actor.{Actor, ActorSystem, Props}
import org.ergoplatform.ErgoSanity.HT
import org.ergoplatform.local.ErgoMiner.{MiningStatusRequest, MiningStatusResponse}
Expand All @@ -12,8 +14,9 @@ import org.ergoplatform.nodeView.state.DigestState
import org.ergoplatform.settings.Constants.hashLength
import org.ergoplatform.settings._
import org.ergoplatform.utils.{ChainGenerator, ErgoGenerators, ErgoTestHelpers}
import scorex.core.app.Version
import scorex.core.network.Handshake
import scorex.core.network.peer.PeerManager
import scorex.core.network.peer.{PeerInfo, PeerManager}
import scorex.core.settings.ScorexSettings
import scorex.core.utils.NetworkTime
import scorex.crypto.authds.ADDigest
Expand All @@ -37,12 +40,37 @@ trait Stubs extends ErgoGenerators with ErgoTestHelpers with ChainGenerator with
DigestState.create(Some(wus.version), Some(wus.rootHash), createTempDir, settings.nodeSettings).get
}
}.sample.get
lazy val memPool = ErgoMemPool.empty

val txs = chain.head.transactions

lazy val memPool = ErgoMemPool.empty.put(txs).get
lazy val readers = Readers(Some(history), Some(state), Some(memPool))


val inetAddr1 = new InetSocketAddress("92.92.92.92",27017)
val inetAddr2 = new InetSocketAddress("93.93.93.93",27017)
val ts1 = System.currentTimeMillis() - 100
val ts2 = System.currentTimeMillis() + 100

val peers = Map(
inetAddr1 -> PeerInfo(ts1, Some(1L), Some("first")),
inetAddr2 -> PeerInfo(ts2, Some(2L), Some("second"))
)

val protocolVersion = Version("1.1.1")

val connectedPeers = Seq(
Handshake("node_pop", protocolVersion, "first", 1L, Some(inetAddr1), ts1),
Handshake("node_pop", protocolVersion, "second", 1L, Some(inetAddr2), ts2)
)

val blacklistedPeers = Seq("4.4.4.4:1111", "8.8.8.8:2222")

class PeersManagerStub extends Actor {
def receive = {
case PeerManager.GetConnectedPeers => sender() ! Seq.empty[Handshake]
case PeerManager.GetConnectedPeers => sender() ! connectedPeers
case PeerManager.GetAllPeers => sender() ! peers
case PeerManager.GetBlacklistedPeers => sender() ! blacklistedPeers
}
}

Expand All @@ -60,6 +88,23 @@ trait Stubs extends ErgoGenerators with ErgoTestHelpers with ChainGenerator with
def props() = Props(new MinerStub)
}

class NodeViewStub extends Actor {
def receive = { case _ => println("hey") }
}

object NodeViewStub {
def props() = Props(new NodeViewStub)
}

class NetworkControllerStub extends Actor {
def receive = { case _ => println("hey") }
}

object NetworkControllerStub {
def props() = Props(new NetworkControllerStub)
}


class ReadersStub extends Actor {
def receive = {
case GetReaders =>
Expand All @@ -75,6 +120,8 @@ trait Stubs extends ErgoGenerators with ErgoTestHelpers with ChainGenerator with
lazy val readersRef = system.actorOf(ReadersStub.props())
lazy val minerRef = system.actorOf(MinerStub.props())
lazy val pmRef = system.actorOf(PeersManagerStub.props())
lazy val nodeViewRef = system.actorOf(NodeViewStub.props())
lazy val networkControllerRef = system.actorOf(NetworkControllerStub.props())

def generateHistory(verifyTransactions: Boolean = true,
ADState: Boolean = true,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.ergoplatform.api.routes

import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest}
import akka.testkit.TestDuration
import io.circe.syntax._
import org.ergoplatform.modifiers.mempool.AnyoneCanSpendTransaction
import org.scalatest.{FlatSpec, Matchers}
import scorex.core.settings.RESTApiSettings

import scala.concurrent.duration._

class TransactionApiRouteSpec extends FlatSpec
with Matchers
with ScalatestRouteTest
with Stubs {

implicit val timeout = RouteTestTimeout(15.seconds dilated)

val restApiSettings = RESTApiSettings("localhost", 8080, None, false, 10 seconds)
val prefix = "/transactions"
val route = TransactionsApiRoute(readersRef, nodeViewRef, restApiSettings, digest = true).route

val tx = AnyoneCanSpendTransaction(IndexedSeq(1L,2L), IndexedSeq(3L, 4L))

//TODO: Not fully implemented yet. There is no codec for tx.
ignore should "post transaction" in {
Post(prefix, tx.json.toString()) ~> route ~> check {
status shouldBe StatusCodes.OK
}
}

//TODO: Not implemented yet
ignore should "get tx by id" in {
Get(prefix + "/txod") ~> route ~> check {
status shouldBe StatusCodes.OK
}
}

it should "get unconfirmed from mempool" in {
Get(prefix + "/unconfirmed") ~> route ~> check {
status shouldBe StatusCodes.OK
memPool.take(50).toSeq.map(_.json).asJson.toString shouldBe responseAs[String]
}
}

}
51 changes: 51 additions & 0 deletions src/test/scala/org/ergoplatform/api/routes/UtilsApiRouteSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.ergoplatform.api.routes

import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest}
import akka.testkit.TestDuration
import org.scalatest.{FlatSpec, Matchers}
import scorex.core.api.http.UtilsApiRoute
import scorex.core.settings.RESTApiSettings

import scala.concurrent.duration._

//TODO move this tests to scorex
class UtilsApiRouteSpec extends FlatSpec
with Matchers
with ScalatestRouteTest
with Stubs {

implicit val timeout = RouteTestTimeout(15.seconds dilated)

val restApiSettings = RESTApiSettings("localhost", 8080, None, false, 10 seconds)
val prefix = "/utils"
val routes = UtilsApiRoute(restApiSettings).route

it should "send random seeds" in {
Get(prefix + "/seed") ~> routes ~> check {
status shouldBe StatusCodes.OK
responseAs[String] should not be empty
}

Get(prefix + "/seed/32") ~> routes ~> check {
status shouldBe StatusCodes.OK
responseAs[String] should not be empty
}

Get(prefix + "/seed/64") ~> routes ~> check {
status shouldBe StatusCodes.OK
responseAs[String] should not be empty
}
}

val msg = "hash_me"

it should "hash string with blake2b" in {
Post(prefix + "/hash/blake2b", msg) ~> routes ~> check {
status shouldBe StatusCodes.OK
responseAs[String] should not be empty
responseAs[String] should not be msg
}
}

}