diff --git a/src/main/resources/api/openapi.yaml b/src/main/resources/api/openapi.yaml index 554dd75303..af4b22671b 100644 --- a/src/main/resources/api/openapi.yaml +++ b/src/main/resources/api/openapi.yaml @@ -5550,6 +5550,41 @@ paths: schema: $ref: '#/components/schemas/ApiError' + /scan/p2sRule: + post: + security: + - ApiKeyAuth: [api_key] + summary: Create and register a scan to track P2S address provided + operationId: scriptP2SRule + tags: + - scan + requestBody: + required: true + content: + application/json: + schema: + type: string + example: '4MQyML64GnzMxZgm' + responses: + '200': + description: Id of custom scan generated and registered + content: + application/json: + schema: + $ref: '#/components/schemas/ScanId' + '400': + description: Bad source + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + default: + description: Error + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + /wallet/generateCommitments: post: security: diff --git a/src/main/scala/org/ergoplatform/http/api/ScanApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/ScanApiRoute.scala index a6110d1369..f99af46995 100644 --- a/src/main/scala/org/ergoplatform/http/api/ScanApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/ScanApiRoute.scala @@ -5,7 +5,7 @@ import akka.http.scaladsl.server.Route import io.circe.Encoder import org.ergoplatform._ import org.ergoplatform.nodeView.wallet._ -import org.ergoplatform.nodeView.wallet.scanning.ScanRequest +import org.ergoplatform.nodeView.wallet.scanning.{EqualsScanningPredicate, ScanRequest, ScanWalletInteraction} import org.ergoplatform.settings.ErgoSettings import scorex.core.api.http.ApiError.BadRequest import scorex.core.api.http.ApiResponse @@ -13,7 +13,10 @@ import scorex.core.settings.RESTApiSettings import scala.util.{Failure, Success} import ScanEntities._ +import org.ergoplatform.ErgoBox.R1 import org.ergoplatform.wallet.Constants.ScanId +import sigmastate.Values.ByteArrayConstant +import sigmastate.serialization.ErgoTreeSerializer /** * This class contains methods to register / deregister and list external scans, and also to serve them. @@ -40,7 +43,8 @@ case class ScanApiRoute(readersHolder: ActorRef, ergoSettings: ErgoSettings) unspentR ~ spentR ~ stopTrackingR ~ - addBoxR + addBoxR ~ + p2sRuleR } def registerR: Route = (path("register") & post & entity(as[ScanRequest])) { request => @@ -91,4 +95,25 @@ case class ScanApiRoute(readersHolder: ActorRef, ergoSettings: ErgoSettings) case Success(_) => ApiResponse(scanIdsBox.box.id) } } + + /** + * API method to get tracking rule corresponding to p2s address + */ + def p2sRuleR: Route = (path("p2sRule") & post & entity(as[String])) { p2sRaw => + val p2s = fromJsonOrPlain(p2sRaw) + addressEncoder.fromString(p2s) match { + case Success(p2sAddr) => + val script = p2sAddr.script + val scriptBytes = ByteArrayConstant(ErgoTreeSerializer.DefaultSerializer.serializeErgoTree(script)) + val trackingRule = EqualsScanningPredicate(R1, scriptBytes) + val request = ScanRequest(p2s, trackingRule, Some(ScanWalletInteraction.Off), Some(true)) + withWalletOp(_.addScan(request).map(_.response)) { + case Failure(e) => BadRequest(s"Bad request $request. ${Option(e.getMessage).getOrElse(e.toString)}") + case Success(app) => ApiResponse(ScanIdWrapper(app.scanId)) + } + case Failure(e) => BadRequest(s"Can't parse $p2s. ${Option(e.getMessage).getOrElse(e.toString)}") + } + } } + + diff --git a/src/test/scala/org/ergoplatform/http/routes/ScanApiRouteSpec.scala b/src/test/scala/org/ergoplatform/http/routes/ScanApiRouteSpec.scala index afee4a72b3..fb4956190f 100644 --- a/src/test/scala/org/ergoplatform/http/routes/ScanApiRouteSpec.scala +++ b/src/test/scala/org/ergoplatform/http/routes/ScanApiRouteSpec.scala @@ -252,7 +252,6 @@ class ScanApiRouteSpec extends AnyFlatSpec } } - it should "stop tracking a box" in { val scanIdBoxId = ScanIdBoxId(ScanId @@ (51: Short), ADKey @@ Random.randomBytes(32)) @@ -261,4 +260,16 @@ class ScanApiRouteSpec extends AnyFlatSpec } } + it should "generate scan for p2s rule" in { + Post(prefix + "/p2sRule", "Ms7smJmdbakqfwNo") ~> route ~> check { + status shouldBe StatusCodes.OK + val res = responseAs[Json] + res.hcursor.downField("scanId").as[Int].toOption.isDefined shouldBe true + } + + Post(prefix + "/p2sRule", "s7smJmdbakqfwNo") ~> route ~> check { + status shouldBe StatusCodes.BadRequest + } + } + }