Skip to content

Commit

Permalink
Merge pull request #1 from Topl/tsdk-733-confirm-redemption
Browse files Browse the repository at this point in the history
TSDK-733 TSDK-738 Confirm redemption
  • Loading branch information
mundacho committed Feb 8, 2024
2 parents e568e04 + 0892f96 commit 8147395
Show file tree
Hide file tree
Showing 29 changed files with 1,475 additions and 178 deletions.
29 changes: 29 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
## Purpose

## Approach

## Testing

## Tickets
*


**Delete everything after and including this line after you are done reading and understand what to put for each section**

>### Purpose
>
>*State in plain words what the scope of the pull request and what problem it is trying to solve*
>
>### Approach
>
>*Enumerate the steps you took to create the code/changes contained in this pull request*
>
>### Testing
>
>*Specify all unit, integration, or end-to-end testing that is contained in this pull request*
>
>### Tickets
>
>*List tickets if any that are connected to this pull request in the following format:*
>
>_* closes #1234_
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/
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
target/
wallet.json
pegin-wallet.json
.bloop/
.bsp/
.metals/
Expand Down
30 changes: 29 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,23 @@ lazy val mavenPublishSettings = List(
)
)

lazy val noPublish = Seq(
publishLocal / skip := true,
publish / skip := true
)

lazy val shared = (project in file("shared"))
.settings(
mavenPublishSettings
)
.settings(
commonSettings,
name := "topl-btc-bridge-shared",
libraryDependencies ++=
Dependencies.toplBtcBridge.main ++
Dependencies.toplBtcBridge.test
)

lazy val toplBtcBridge = (project in file("topl-btc-bridge"))
.settings(
if (sys.env.get("DOCKER_PUBLISH").getOrElse("false").toBoolean)
Expand All @@ -125,6 +142,7 @@ lazy val toplBtcBridge = (project in file("topl-btc-bridge"))
Dependencies.toplBtcBridge.test
)
.enablePlugins(DockerPlugin, JavaAppPackaging)
.dependsOn(shared)

lazy val toplBtcCli = (project in file("topl-btc-cli"))
.settings(mavenPublishSettings)
Expand All @@ -135,4 +153,14 @@ lazy val toplBtcCli = (project in file("topl-btc-cli"))
Dependencies.toplBtcBridge.main ++
Dependencies.toplBtcBridge.test
)
.enablePlugins(DockerPlugin, JavaAppPackaging)
.enablePlugins(JavaAppPackaging)
.dependsOn(shared)

lazy val root = project
.in(file("."))
.settings(
organization := "co.topl",
name := "topl-btc-bridge-umbrella",
)
.settings(noPublish)
.aggregate(toplBtcBridge, toplBtcCli)
6 changes: 4 additions & 2 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ object Dependencies {

val catsCoreVersion = "2.10.0"

lazy val http4sVersion = "0.23.18"
lazy val http4sVersion = "0.23.23"

val logback: Seq[ModuleID] = Seq(
"ch.qos.logback" % "logback-classic" % "1.4.11"
Expand Down Expand Up @@ -48,7 +48,9 @@ object Dependencies {

lazy val http4s: Seq[ModuleID] = Seq(
"org.http4s" %% "http4s-ember-client" % http4sVersion,
"org.http4s" %% "http4s-circe" % http4sVersion
"org.http4s" %% "http4s-dsl" % http4sVersion,
"org.http4s" %% "http4s-circe" % http4sVersion,
"org.http4s" %% "http4s-ember-server" % http4sVersion
)

lazy val bitcoinS: Seq[ModuleID] = Seq(
Expand Down
32 changes: 32 additions & 0 deletions shared/src/main/scala/co/topl/shared/InputOutput.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package co.topl.shared

case class StartSessionRequest(
pkey: String,
sha256: String
)

case class StartSessionResponse(
sessionID: String,
script: String,
escrowAddress: String,
descriptor: String
)

case class ConfirmRedemptionRequest(
sessionID: String,
inputTxId: String,
inputIndex: Int,
feePerByte: Int,
amount: Long,
secret: String
)

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
30 changes: 30 additions & 0 deletions shared/src/main/scala/co/topl/shared/NetworkIdentifiers.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package co.topl.shared


sealed abstract class BitcoinNetworkIdentifiers(
val name: String
) {
override def toString: String = name
def btcNetwork: org.bitcoins.core.config.BitcoinNetwork =
this match {
case Mainnet => org.bitcoins.core.config.MainNet
case Testnet => org.bitcoins.core.config.TestNet3
case RegTest => org.bitcoins.core.config.RegTest
}
}

case object Mainnet extends BitcoinNetworkIdentifiers("mainnet")
case object Testnet extends BitcoinNetworkIdentifiers("testnet")
case object RegTest extends BitcoinNetworkIdentifiers("regtest")
case object BitcoinNetworkIdentifiers {

def values = Set(Mainnet, Testnet, RegTest)

def fromString(s: String): Option[BitcoinNetworkIdentifiers] =
s match {
case "mainnet" => Some(Mainnet)
case "testnet" => Some(Testnet)
case "regtest" => Some(RegTest)
case _ => None
}
}
16 changes: 16 additions & 0 deletions shared/src/main/scala/co/topl/shared/ParamParser.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package co.topl.shared

object ParamParser {

implicit val networkRead: scopt.Read[BitcoinNetworkIdentifiers] =
scopt.Read
.reads(BitcoinNetworkIdentifiers.fromString(_))
.map(_ match {
case Some(value) => value
case None =>
throw new IllegalArgumentException(
"Invalid network. Possible values: mainnet, testnet, regtest"
)
})

}
112 changes: 112 additions & 0 deletions shared/src/main/scala/co/topl/shared/utils/KeyGenerationUtils.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package co.topl.shared.utils

import cats.effect.kernel.Sync
import co.topl.shared.BitcoinNetworkIdentifiers
import org.bitcoins.core.crypto.MnemonicCode
import org.bitcoins.core.hd.BIP32Path
import org.bitcoins.core.hd.HDAccount
import org.bitcoins.core.hd.HDPath
import org.bitcoins.core.hd.HDPurposes
import org.bitcoins.core.wallet.keymanagement.KeyManagerParams
import org.bitcoins.crypto.AesPassword
import org.bitcoins.keymanager.bip39.BIP39KeyManager
import scodec.bits.ByteVector
import org.bitcoins.crypto.ECDigitalSignature
import org.bitcoins.crypto.HashType
import org.bitcoins.crypto.ECPublicKey

object KeyGenerationUtils {

def signWithKeyManager[F[_]: Sync](
km: BIP39KeyManager,
txBytes: ByteVector,
currentIdx: Int
): F[String] = {
import cats.implicits._
for {
signed <- Sync[F].delay(
km.toSign(HDPath.fromString("m/84'/1'/0'/0/" + currentIdx))
.sign(txBytes)
)
canonicalSignature <- Sync[F].delay(
ECDigitalSignature(
signed.bytes ++ ByteVector.fromByte(HashType.sigHashAll.byte)
)
)
} yield canonicalSignature.hex
}
def loadKeyManager[F[_]: Sync](
btcNetwork: BitcoinNetworkIdentifiers,
seedFile: String,
password: String
): F[BIP39KeyManager] = {
import cats.implicits._
for {
seedPath <- Sync[F].delay(
new java.io.File(seedFile).getAbsoluteFile.toPath
)
purpose = HDPurposes.SegWit
kmParams = KeyManagerParams(seedPath, purpose, btcNetwork.btcNetwork)
aesPasswordOpt = Some(AesPassword.fromString(password))
km <- Sync[F].fromEither(
BIP39KeyManager
.fromParams(
kmParams,
aesPasswordOpt,
None
)
.left
.map(_ => new IllegalArgumentException("Invalid params"))
)
} yield km
}

def createKeyManager[F[_]: Sync](
btcNetwork: BitcoinNetworkIdentifiers,
seedFile: String,
password: String
) = {
import cats.implicits._
for {
seedPath <- Sync[F].delay(
new java.io.File(seedFile).getAbsoluteFile.toPath
)
purpose = HDPurposes.SegWit
kmParams = KeyManagerParams(seedPath, purpose, btcNetwork.btcNetwork)
aesPasswordOpt = Some(AesPassword.fromString(password))
entropy = MnemonicCode.getEntropy256Bits
mnemonic = MnemonicCode.fromEntropy(entropy)
km <- Sync[F].fromEither(
BIP39KeyManager.initializeWithMnemonic(
aesPasswordOpt,
mnemonic,
None,
kmParams
)
)
} yield km
}

def generateKey[F[_]: Sync](
km: BIP39KeyManager,
currentIdx: Int
): F[ECPublicKey] = {
import cats.implicits._
for {
hdAccount <- Sync[F].fromOption(
HDAccount.fromPath(
BIP32Path.fromString("m/84'/1'/0'")
) // this is the standard account path for segwit
,
new IllegalArgumentException("Invalid account path")
)
pKey <- Sync[F].delay(
km.deriveXPub(hdAccount)
.get
.deriveChildPubKey(BIP32Path.fromString("m/0/" + currentIdx.toString))
.get
.key
)
} yield (pKey)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package co.topl.bridge

import co.topl.shared.BitcoinNetworkIdentifiers
import scopt.OParser

trait BridgeParamsDescriptor {

import co.topl.shared.ParamParser._

val builder = OParser.builder[ToplBTCBridgeParamConfig]

val parser = {
import builder._

OParser.sequence(
programName("topl-btc-bridge"),
head("topl-btc-bridge", "0.1"),
opt[BitcoinNetworkIdentifiers]('n', "network")
.action((x, c) => c.copy(btcNetwork = x))
.text(
"Network name: Possible values: mainnet, testnet, regtest. (mandatory)"
),
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)"
),
opt[String]("peg-in-seed-file")
.action((x, c) => c.copy(pegInSeedFile = x))
.text(
"The path to the peg inn seed file. (default: pegin-wallet.json)"
),
opt[String]("peg-in-password")
.action((x, c) => c.copy(pegInPassword = x))
.text(
"The password to the seed file. (default: password)"
),
opt[String]("seed-file")
.action((x, c) => c.copy(walletSeedFile = x))
.text(
"The path to the seed file. (default: wallet.json)"
),
opt[String]("password")
.action((x, c) => c.copy(walletPassword = x))
.text(
"The password to the seed file. (default: password)"
),
)
}

}

0 comments on commit 8147395

Please sign in to comment.