Skip to content

Commit

Permalink
TSDK-733 Implement basic confirm redemption.
Browse files Browse the repository at this point in the history
  • Loading branch information
mundacho committed Jan 30, 2024
1 parent e568e04 commit 320610e
Show file tree
Hide file tree
Showing 14 changed files with 785 additions and 175 deletions.
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
26 changes: 26 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,26 @@
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
)
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"
)
})

}
101 changes: 101 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,101 @@
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

object KeyGenerationUtils {

def loadKeyAndSign[F[_]: Sync](
btcNetwork: BitcoinNetworkIdentifiers,
seedFile: String,
password: String,
txBytes: ByteVector
): F[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
.fromParams(
kmParams,
aesPasswordOpt,
None
)
.left
.map(_ => new IllegalArgumentException("Invalid params"))
)
// 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")
// )
signed <- Sync[F].delay(
km.toSign(HDPath.fromString("m/84'/1'/0'/0/0")).sign(txBytes)
)
canonicalSignature <- Sync[F].delay(ECDigitalSignature(
signed.bytes ++ ByteVector.fromByte(HashType.sigHashAll.byte)
))
} yield canonicalSignature.hex
}

def generateKey[F[_]: Sync](
btcNetwork: BitcoinNetworkIdentifiers,
seedFile: String,
password: String
): F[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
)
)
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/0"))
.get
.key
.hex
)
} yield (pKey)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
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)"
)
)
}

}

0 comments on commit 320610e

Please sign in to comment.