Skip to content

Commit

Permalink
TSDK-733 Add tests and GH actions.
Browse files Browse the repository at this point in the history
  • Loading branch information
mundacho committed Feb 5, 2024
1 parent b6e3544 commit 711774a
Show file tree
Hide file tree
Showing 12 changed files with 310 additions and 68 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/sbt_checkPR.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Check and Deploy
on:
push:
branches: ['*']
tags: ["*"]

jobs:
unit-test:
name: Run Unit Tests
runs-on: ubuntu-20.04
steps:
- name: Checkout code
uses: actions/checkout@v3.0.2
with:
fetch-depth: 0
- name: Setup Scala
uses: olafurpg/setup-scala@v13
with:
java-version: adopt@1.11
- name: Run unit tests
run: sbt toplBtcBridge/test
- uses: actions/upload-artifact@v3
with:
name: unit-test-reports
path: topl-btc-bridge/target/test-reports/
8 changes: 7 additions & 1 deletion shared/src/main/scala/co/topl/shared/InputOutput.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,10 @@ case class ConfirmRedemptionRequest(

case class ConfirmRedemptionResponse(
tx: String
)
)

sealed trait BridgeError extends Throwable

case class SessionNotFoundError(error: String) extends BridgeError
case class InvalidKey(error: String) extends BridgeError
case class InvalidHash(error: String) extends BridgeError
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ trait BridgeParamsDescriptor {
.text(
"Network name: Possible values: mainnet, testnet, regtest. (mandatory)"
),
opt[Int]("blocks-to-redeem")
.action((x, c) => c.copy(blockToRedeem = x))
opt[Int]("blocks-to-recover")
.action((x, c) => c.copy(blockToRecover = x))
.text(
"The number of blocks that the user needs to wait before they can reclaim their funds. (default: 100)"
),
Expand Down
21 changes: 17 additions & 4 deletions topl-btc-bridge/src/main/scala/co/topl/bridge/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ import co.topl.bridge.managers.BTCWalletImpl
import co.topl.bridge.managers.SessionManagerAlgebra
import co.topl.bridge.managers.SessionManagerImpl
import co.topl.shared.BitcoinNetworkIdentifiers
import co.topl.shared.ConfirmRedemptionRequest
import co.topl.shared.SessionNotFoundError
import co.topl.shared.StartSessionRequest
import co.topl.shared.utils.KeyGenerationUtils
import io.circe.generic.auto._
import io.circe.syntax._
import org.http4s.HttpRoutes
import org.http4s._
import org.http4s.circe._
Expand All @@ -28,7 +31,7 @@ import org.http4s.server.staticcontent.resourceServiceBuilder
import scopt.OParser

import java.util.concurrent.ConcurrentHashMap
import co.topl.shared.ConfirmRedemptionRequest
import co.topl.shared.BridgeError

object Main extends IOApp with BridgeParamsDescriptor {

Expand All @@ -39,22 +42,26 @@ object Main extends IOApp with BridgeParamsDescriptor {
sessionManager: SessionManagerAlgebra[IO],
pegInWalletManager: BTCWalletAlgebra[IO],
walletManager: BTCWalletAlgebra[IO],
blockToRecover: Int,
btcNetwork: BitcoinNetworkIdentifiers
) = HttpRoutes.of[IO] {
case req @ POST -> Root / "start-session" =>
implicit val startSessionRequestDecoder
: EntityDecoder[IO, StartSessionRequest] =
jsonOf[IO, StartSessionRequest]
import io.circe.syntax._
for {
x <- req.as[StartSessionRequest]
res <- startSession(
x,
pegInWalletManager,
sessionManager,
blockToRecover,
btcNetwork
)
resp <- Ok(res.asJson)
resp <- res match {
case Left(e: BridgeError) => BadRequest(e.asJson)
case Right(value) => Ok(value.asJson)
}
} yield resp
case req @ POST -> Root / "confirm-redemption" =>
implicit val confirmRedemptionRequestDecoder
Expand All @@ -68,7 +75,12 @@ object Main extends IOApp with BridgeParamsDescriptor {
walletManager,
sessionManager
)
} yield res
resp <- res match {
case Left(e: SessionNotFoundError) => NotFound(e.asJson)
case Left(e: BridgeError) => Ok(e.asJson)
case Right(value) => Ok(value.asJson)
}
} yield resp
}

override def run(args: List[String]): IO[ExitCode] = {
Expand Down Expand Up @@ -126,6 +138,7 @@ object Main extends IOApp with BridgeParamsDescriptor {
sessionManager,
pegInWalletManager,
walletManager,
params.blockToRecover,
params.btcNetwork
)
)(default = staticAssetsService)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import co.topl.shared.RegTest
import co.topl.shared.BitcoinNetworkIdentifiers

case class ToplBTCBridgeParamConfig(
blockToRedeem: Int =
blockToRecover: Int =
100, // the number of blocks to wait before the user can reclaim their funds
pegInSeedFile: String = "pegin-wallet.json",
pegInPassword: String = "password",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package co.topl.bridge.controllers

import cats.effect.kernel.Async
import cats.effect.kernel.Sync
import co.topl.bridge.managers.BTCWalletAlgebra
import co.topl.bridge.managers.SessionManagerAlgebra
import co.topl.bridge.utils.BitcoinUtils
import co.topl.shared.ConfirmRedemptionRequest
import co.topl.shared.ConfirmRedemptionResponse
import io.circe.generic.auto._
import org.bitcoins.core.currency.SatoshisLong
import org.bitcoins.core.protocol.script.NonStandardScriptSignature
import org.bitcoins.core.protocol.script.P2WSHWitnessV0
Expand All @@ -16,23 +13,28 @@ import org.bitcoins.core.protocol.transaction.WitnessTransaction
import org.bitcoins.core.script.constant.OP_0
import org.bitcoins.core.script.constant.ScriptConstant
import org.bitcoins.crypto._
import org.http4s.circe._
import scodec.bits.ByteVector
import co.topl.shared.SessionNotFoundError
import cats.effect.kernel.Sync
import co.topl.shared.BridgeError

object ConfirmRedemptionController {

def confirmRedemption[F[_]: Async](
def confirmRedemption[F[_]: Sync](
req: ConfirmRedemptionRequest,
pegInWalletManager: BTCWalletAlgebra[F],
walletManager: BTCWalletAlgebra[F],
sessionManager: SessionManagerAlgebra[F]
) = {
import io.circe.syntax._
): F[Either[BridgeError, ConfirmRedemptionResponse]] = {
import cats.implicits._
val dsl = org.http4s.dsl.Http4sDsl[F]
import dsl._
(for {
sessionInfo <- sessionManager.getSession(req.sessionID)
sessionInfo <- sessionManager
.getSession(req.sessionID)
.handleError(_ =>
throw SessionNotFoundError(
s"Session with id ${req.sessionID} not found"
)
)
nextPubKey <- walletManager.getCurrentPubKey()
tx = BitcoinUtils.createRedeemingTx(
req.inputTxId,
Expand Down Expand Up @@ -69,14 +71,8 @@ object ConfirmRedemptionController {
bridgeSig
)
)
resp <- Ok(
ConfirmRedemptionResponse(
txWit.hex
).asJson
)
} yield resp).handleErrorWith(e => {
Sync[F].delay(e.printStackTrace()) *>
BadRequest("Error")
})
} yield ConfirmRedemptionResponse(txWit.hex).asRight[BridgeError]).recover {
case e: BridgeError => Left(e)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,42 +17,55 @@ import org.bitcoins.core.util.BytesUtil
import org.bitcoins.crypto.ECPublicKey
import org.bitcoins.crypto._
import scodec.bits.ByteVector
import co.topl.shared.BridgeError
import cats.effect.kernel.Sync
import co.topl.shared.InvalidHash
import co.topl.shared.InvalidKey

object StartSessionController {

private def createSessionInfo(
private def createSessionInfo[F[_]: Sync](
currentWalletIdx: Int,
sha256: String,
userPKey: String,
pUserPKey: String,
bridgePKey: String,
blockToRecover: Int,
btcNetwork: BitcoinNetworkIdentifiers
): SessionInfo = {
val hash = ByteVector.fromHex(sha256).get
val asm =
BitcoinUtils.buildScriptAsm(
ECPublicKey.fromHex(userPKey),
ECPublicKey.fromHex(bridgePKey),
hash,
1000L
)
val scriptAsm = BytesUtil.toByteVector(asm)
val scriptHash = CryptoUtil.sha256(scriptAsm)
val push_op = BitcoinScriptUtil.calculatePushOp(hash)
val address = Bech32Address
.apply(
WitnessScriptPubKey
.apply(
Seq(OP_0) ++
push_op ++
Seq(ScriptConstant.fromBytes(scriptHash.bytes))
),
btcNetwork.btcNetwork
): F[SessionInfo] = {
import cats.implicits._
for {
hash <- Sync[F].fromOption(
ByteVector.fromHex(sha256),
InvalidHash(s"Invalid hash $sha256")
)
.value
SessionInfo(
userPKey <- Sync[F]
.delay(ECPublicKey.fromHex(pUserPKey))
.handleError(_ => throw InvalidKey(s"Invalid key $pUserPKey"))
asm =
BitcoinUtils.buildScriptAsm(
userPKey,
ECPublicKey.fromHex(bridgePKey),
hash,
blockToRecover
)
scriptAsm = BytesUtil.toByteVector(asm)
scriptHash = CryptoUtil.sha256(scriptAsm)
push_op = BitcoinScriptUtil.calculatePushOp(hash)
address = Bech32Address
.apply(
WitnessScriptPubKey
.apply(
Seq(OP_0) ++
push_op ++
Seq(ScriptConstant.fromBytes(scriptHash.bytes))
),
btcNetwork.btcNetwork
)
.value
} yield SessionInfo(
bridgePKey,
currentWalletIdx,
userPKey,
userPKey.hex,
sha256,
scriptAsm.toHex,
address
Expand All @@ -63,17 +76,19 @@ object StartSessionController {
req: StartSessionRequest,
pegInWalletManager: BTCWalletAlgebra[F],
sessionManager: SessionManagerAlgebra[F],
blockToRecover: Int,
btcNetwork: BitcoinNetworkIdentifiers
) = {
): F[Either[BridgeError, StartSessionResponse]] = {
import cats.implicits._
(for {
idxAndnewKey <- pegInWalletManager.getCurrentPubKeyAndPrepareNext()
(idx, newKey) = idxAndnewKey
sessionInfo = createSessionInfo(
sessionInfo <- createSessionInfo(
idx,
req.sha256,
req.pkey,
newKey.hex,
blockToRecover,
btcNetwork
)
sessionId <- sessionManager.createNewSession(sessionInfo)
Expand All @@ -82,7 +97,9 @@ object StartSessionController {
sessionInfo.scriptAsm,
sessionInfo.address,
BitcoinUtils.createDescriptor(newKey.hex, req.pkey, req.sha256)
))
).asRight[BridgeError]).handleError { case e: BridgeError =>
Left(e)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ object BTCWalletImpl {
override def getCurrentPubKeyAndPrepareNext(): F[(Int, ECPublicKey)] = {
for {
idx <- currentIdx.getAndUpdate(_ + 1)
nextIdx <- currentIdx.get
_ = println("idx: " + nextIdx)
pubKey <- KeyGenerationUtils.generateKey(km, idx)
} yield (idx, pubKey)
}
Expand Down
1 change: 1 addition & 0 deletions topl-btc-bridge/src/test/resources/pegin-wallet.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"iv":"197197601a0a3ec95413c411df52684e","cipherText":"dab826df78d4bfb7fb76d82b3f8cc75882a4d338373d1750a5117e50541a4bb356b7999c2d150704a761719d690e24794182be6e6b13fdfe13568d47d899a374b25464417ba9b89dbe7232412f5cfe33b6e0fa711bc06ee197c8407aeca884215b3cdc7cd52b2a344fae82c18581653387183c0b28ff901dff9fa69a939661851cbe04709a6c272cbed5e1975866","salt":"906dc495a1d2f2ba94f3c2a6f30240ed9ad61e7b46f5460fc1cdb1007a59432f","creationTime":1706812599,"backupTime":null,"imported":false}

0 comments on commit 711774a

Please sign in to comment.