mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-19 01:40:55 +01:00
Move DLC Oracle module to master
(#2083)
This commit is contained in:
parent
67b6c7b172
commit
2d34e2b690
@ -0,0 +1,25 @@
|
||||
package org.bitcoins.commons.jsonmodels.dlc
|
||||
|
||||
import org.bitcoins.crypto.StringFactory
|
||||
|
||||
sealed abstract class SigningVersion
|
||||
|
||||
object SigningVersion extends StringFactory[SigningVersion] {
|
||||
|
||||
/** Initial signing version that was created with krystal bull, not a part of any spec */
|
||||
final case object Mock extends SigningVersion
|
||||
|
||||
val all: Vector[SigningVersion] = Vector(Mock)
|
||||
|
||||
override def fromStringOpt(str: String): Option[SigningVersion] = {
|
||||
all.find(state => str.toLowerCase() == state.toString.toLowerCase)
|
||||
}
|
||||
|
||||
override def fromString(string: String): SigningVersion = {
|
||||
fromStringOpt(string) match {
|
||||
case Some(state) => state
|
||||
case None =>
|
||||
sys.error(s"Could not find signing version for string=${string}")
|
||||
}
|
||||
}
|
||||
}
|
47
build.sbt
47
build.sbt
@ -10,6 +10,7 @@ flywayClean / aggregate := false
|
||||
Test / flywayClean / aggregate := true
|
||||
|
||||
lazy val Benchmark = config("bench") extend Test
|
||||
|
||||
lazy val benchSettings: Seq[Def.SettingsDefinition] = {
|
||||
//for scalameter
|
||||
//https://scalameter.github.io/home/download/
|
||||
@ -31,6 +32,7 @@ lazy val benchSettings: Seq[Def.SettingsDefinition] = {
|
||||
import Projects._
|
||||
lazy val crypto = project in file("crypto")
|
||||
lazy val core = project in file("core") dependsOn crypto
|
||||
|
||||
lazy val bitcoindRpc = project
|
||||
.in(file("bitcoind-rpc"))
|
||||
.settings(CommonSettings.prodSettings: _*)
|
||||
@ -57,6 +59,8 @@ lazy val `bitcoin-s` = project
|
||||
dbCommonsTest,
|
||||
feeProvider,
|
||||
feeProviderTest,
|
||||
dlcOracle,
|
||||
dlcOracleTest,
|
||||
bitcoindRpc,
|
||||
bitcoindRpcTest,
|
||||
bench,
|
||||
@ -285,6 +289,7 @@ lazy val gui = project
|
||||
)
|
||||
|
||||
lazy val chainDbSettings = dbFlywaySettings("chaindb")
|
||||
|
||||
lazy val chain = project
|
||||
.in(file("chain"))
|
||||
.settings(CommonSettings.prodSettings: _*)
|
||||
@ -375,6 +380,7 @@ lazy val eclairRpcTest = project
|
||||
.dependsOn(core % testAndCompile, testkit)
|
||||
|
||||
lazy val nodeDbSettings = dbFlywaySettings("nodedb")
|
||||
|
||||
lazy val node =
|
||||
project
|
||||
.in(file("node"))
|
||||
@ -461,6 +467,7 @@ lazy val keyManagerTest = project
|
||||
.dependsOn(keyManager, testkit)
|
||||
|
||||
lazy val walletDbSettings = dbFlywaySettings("walletdb")
|
||||
|
||||
lazy val wallet = project
|
||||
.in(file("wallet"))
|
||||
.settings(CommonSettings.prodSettings: _*)
|
||||
@ -483,8 +490,27 @@ lazy val walletTest = project
|
||||
.dependsOn(core % testAndCompile, testkit, wallet)
|
||||
.enablePlugins(FlywayPlugin)
|
||||
|
||||
lazy val dlcOracle = project
|
||||
.in(file("dlc-oracle"))
|
||||
.settings(CommonSettings.prodSettings: _*)
|
||||
.settings(
|
||||
name := "bitcoin-s-dlc-oracle",
|
||||
libraryDependencies ++= Deps.dlcOracle
|
||||
)
|
||||
.dependsOn(core, keyManager, dbCommons)
|
||||
|
||||
lazy val dlcOracleTest = project
|
||||
.in(file("dlc-oracle-test"))
|
||||
.settings(CommonSettings.testSettings: _*)
|
||||
.settings(
|
||||
name := "bitcoin-s-dlc-oracle-test",
|
||||
libraryDependencies ++= Deps.dlcOracleTest
|
||||
)
|
||||
.dependsOn(core % testAndCompile, dlcOracle, testkit)
|
||||
|
||||
/** Given a database name, returns the appropriate
|
||||
* Flyway settings we apply to a project (chain, node, wallet) */
|
||||
* Flyway settings we apply to a project (chain, node, wallet)
|
||||
*/
|
||||
def dbFlywaySettings(dbName: String): List[Setting[_]] = {
|
||||
lazy val DB_HOST = "localhost"
|
||||
lazy val DB_NAME = s"${dbName}.sqlite"
|
||||
@ -505,15 +531,16 @@ def dbFlywaySettings(dbName: String): List[Setting[_]] = {
|
||||
db.createNewFile()
|
||||
}
|
||||
|
||||
def makeNetworkSettings(directoryPath: String): List[Setting[_]] = List(
|
||||
Test / flywayUrl := s"jdbc:sqlite:$directoryPath$DB_NAME",
|
||||
Test / flywayLocations := List("nodedb/migration"),
|
||||
Test / flywayUser := "nodedb",
|
||||
Test / flywayPassword := "",
|
||||
flywayUrl := s"jdbc:sqlite:$directoryPath$DB_NAME",
|
||||
flywayUser := "nodedb",
|
||||
flywayPassword := ""
|
||||
)
|
||||
def makeNetworkSettings(directoryPath: String): List[Setting[_]] =
|
||||
List(
|
||||
Test / flywayUrl := s"jdbc:sqlite:$directoryPath$DB_NAME",
|
||||
Test / flywayLocations := List("nodedb/migration"),
|
||||
Test / flywayUser := "nodedb",
|
||||
Test / flywayPassword := "",
|
||||
flywayUrl := s"jdbc:sqlite:$directoryPath$DB_NAME",
|
||||
flywayUser := "nodedb",
|
||||
flywayPassword := ""
|
||||
)
|
||||
|
||||
lazy val mainnet = makeNetworkSettings(mainnetDir)
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.bitcoins.db
|
||||
|
||||
import org.bitcoins.commons.jsonmodels.dlc.DLCMessage.ContractInfo
|
||||
import org.bitcoins.commons.jsonmodels.dlc.SigningVersion
|
||||
import org.bitcoins.core.config.{BitcoinNetwork, BitcoinNetworks}
|
||||
import org.bitcoins.core.crypto._
|
||||
import org.bitcoins.core.currency.{CurrencyUnit, Satoshis}
|
||||
@ -95,9 +96,19 @@ class DbCommonsColumnMappers(val profile: JdbcProfile) {
|
||||
implicit val sha256DigestBEMapper: BaseColumnType[Sha256DigestBE] =
|
||||
MappedColumnType.base[Sha256DigestBE, String](_.hex, Sha256DigestBE.fromHex)
|
||||
|
||||
implicit val sha256DigestMapper: BaseColumnType[Sha256Digest] =
|
||||
MappedColumnType.base[Sha256Digest, String](_.hex, Sha256Digest.fromHex)
|
||||
|
||||
implicit val ecPublicKeyMapper: BaseColumnType[ECPublicKey] =
|
||||
MappedColumnType.base[ECPublicKey, String](_.hex, ECPublicKey.fromHex)
|
||||
|
||||
implicit val fieldElementMapper: BaseColumnType[FieldElement] =
|
||||
MappedColumnType.base[FieldElement, String](_.hex, FieldElement.fromHex)
|
||||
|
||||
implicit val signingVersionMapper: BaseColumnType[SigningVersion] =
|
||||
MappedColumnType.base[SigningVersion, String](_.toString,
|
||||
SigningVersion.fromString)
|
||||
|
||||
implicit val schnorrPublicKeyMapper: BaseColumnType[SchnorrPublicKey] =
|
||||
MappedColumnType
|
||||
.base[SchnorrPublicKey, String](_.hex, SchnorrPublicKey.fromHex)
|
||||
|
@ -0,0 +1,16 @@
|
||||
package org.bitcoins.dlc.oracle
|
||||
|
||||
import org.bitcoins.commons.jsonmodels.dlc.SigningVersion
|
||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||
|
||||
class SigningVersionTest extends BitcoinSUnitTest {
|
||||
|
||||
behavior of "SigningVersion"
|
||||
|
||||
it must "read from string" in {
|
||||
SigningVersion.fromString("mock") must be(SigningVersion.Mock)
|
||||
SigningVersion.fromString("MoCk") must be(SigningVersion.Mock)
|
||||
SigningVersion.fromString("MOCK") must be(SigningVersion.Mock)
|
||||
SigningVersion.fromStringOpt("not a real signing version") must be(None)
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
CREATE TABLE r_values
|
||||
(
|
||||
nonce TEXT NOT NULL,
|
||||
label TEXT NOT NULL UNIQUE,
|
||||
hd_purpose INTEGER NOT NULL,
|
||||
coin INTEGER NOT NULL,
|
||||
account_index INTEGER NOT NULL,
|
||||
chain_type INTEGER NOT NULL,
|
||||
key_index INTEGER NOT NULL,
|
||||
commitment_signature TEXT NOT NULL,
|
||||
PRIMARY KEY (nonce)
|
||||
);
|
||||
|
||||
CREATE TABLE events
|
||||
(
|
||||
nonce TEXT NOT NULL,
|
||||
label TEXT NOT NULL UNIQUE,
|
||||
num_outcomes INTEGER NOT NULL,
|
||||
signing_version TEXT NOT NULL,
|
||||
attestation TEXT,
|
||||
CONSTRAINT fk_label FOREIGN KEY (label) REFERENCES r_values (label) on update NO ACTION on delete NO ACTION,
|
||||
PRIMARY KEY (nonce),
|
||||
CONSTRAINT fk_nonce FOREIGN KEY (nonce) REFERENCES r_values (nonce) on update NO ACTION on delete NO ACTION
|
||||
);
|
||||
|
||||
CREATE TABLE event_outcomes
|
||||
(
|
||||
nonce TEXT NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
hashed_message TEXT NOT NULL,
|
||||
CONSTRAINT fk_nonce FOREIGN KEY (nonce) REFERENCES events (nonce) on update NO ACTION on delete NO ACTION
|
||||
);
|
@ -0,0 +1,32 @@
|
||||
CREATE TABLE `r_values`
|
||||
(
|
||||
`nonce` VARCHAR(254) NOT NULL,
|
||||
`label` VARCHAR(254) NOT NULL UNIQUE,
|
||||
`hd_purpose` INTEGER NOT NULL,
|
||||
`coin` INTEGER NOT NULL,
|
||||
`account_index` INTEGER NOT NULL,
|
||||
`chain_type` INTEGER NOT NULL,
|
||||
`key_index` INTEGER NOT NULL,
|
||||
`commitment_signature` VARCHAR(254) NOT NULL,
|
||||
PRIMARY KEY (`nonce`)
|
||||
);
|
||||
|
||||
CREATE TABLE `events`
|
||||
(
|
||||
`nonce` VARCHAR(254) NOT NULL,
|
||||
`label` VARCHAR(254) NOT NULL UNIQUE,
|
||||
`num_outcomes` INTEGER NOT NULL,
|
||||
`signing_version` VARCHAR(254) NOT NULL,
|
||||
`attestation` VARCHAR(254),
|
||||
CONSTRAINT `fk_label` FOREIGN KEY (`label`) REFERENCES `r_values` (`label`) on update NO ACTION on delete NO ACTION,
|
||||
PRIMARY KEY (`nonce`),
|
||||
CONSTRAINT `fk_nonce` FOREIGN KEY (`nonce`) REFERENCES `r_values` (`nonce`) on update NO ACTION on delete NO ACTION
|
||||
);
|
||||
|
||||
CREATE TABLE `event_outcomes`
|
||||
(
|
||||
`nonce` VARCHAR(254) NOT NULL,
|
||||
`message` VARCHAR(254) NOT NULL,
|
||||
`hashed_message` VARCHAR(254) NOT NULL,
|
||||
CONSTRAINT `fk_nonce` FOREIGN KEY (`nonce`) REFERENCES `events` (`nonce`) on update NO ACTION on delete NO ACTION
|
||||
);
|
@ -0,0 +1,82 @@
|
||||
package org.bitcoins.dlc.oracle
|
||||
|
||||
import java.nio.file.{Files, Path}
|
||||
|
||||
import com.typesafe.config.Config
|
||||
import org.bitcoins.core.config.NetworkParameters
|
||||
import org.bitcoins.core.util.FutureUtil
|
||||
import org.bitcoins.db.{AppConfig, DbManagement, JdbcProfileComponent}
|
||||
import org.bitcoins.dlc.oracle.storage._
|
||||
import org.bitcoins.keymanager.WalletStorage
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
case class DLCOracleAppConfig(
|
||||
private val directory: Path,
|
||||
private val conf: Config*)(implicit val ec: ExecutionContext)
|
||||
extends AppConfig
|
||||
with DbManagement
|
||||
with JdbcProfileComponent[DLCOracleAppConfig] {
|
||||
|
||||
import profile.api._
|
||||
|
||||
override def appConfig: DLCOracleAppConfig = this
|
||||
|
||||
override type ConfigType = DLCOracleAppConfig
|
||||
|
||||
override def newConfigOfType(
|
||||
configOverrides: Seq[Config]): DLCOracleAppConfig =
|
||||
DLCOracleAppConfig(directory, configOverrides: _*)
|
||||
|
||||
override def moduleName: String = "oracle"
|
||||
|
||||
override def baseDatadir: Path = directory
|
||||
|
||||
lazy val networkParameters: NetworkParameters = chain.network
|
||||
|
||||
/** The path to our encrypted mnemonic seed */
|
||||
lazy val seedPath: Path = {
|
||||
baseDatadir.resolve(WalletStorage.ENCRYPTED_SEED_FILE_NAME)
|
||||
}
|
||||
|
||||
override def start(): Future[Unit] = {
|
||||
logger.debug(s"Initializing wallet setup")
|
||||
|
||||
if (Files.notExists(datadir)) {
|
||||
Files.createDirectories(datadir)
|
||||
}
|
||||
|
||||
val numMigrations = {
|
||||
migrate()
|
||||
}
|
||||
|
||||
logger.info(s"Applied $numMigrations to the wallet project")
|
||||
|
||||
FutureUtil.unit
|
||||
}
|
||||
|
||||
/** Checks if our oracle as a mnemonic seed associated with it */
|
||||
def seedExists(): Boolean = {
|
||||
WalletStorage.seedExists(seedPath)
|
||||
}
|
||||
|
||||
def exists(): Boolean = {
|
||||
seedExists() &&
|
||||
Files.exists(baseDatadir.resolve("oracle.sqlite"))
|
||||
}
|
||||
|
||||
private val rValueTable: TableQuery[Table[_]] = {
|
||||
RValueDAO()(ec, appConfig).table
|
||||
}
|
||||
|
||||
private val eventTable: TableQuery[Table[_]] = {
|
||||
EventDAO()(ec, appConfig).table
|
||||
}
|
||||
|
||||
private val eventOutcomeTable: TableQuery[Table[_]] = {
|
||||
EventOutcomeDAO()(ec, appConfig).table
|
||||
}
|
||||
|
||||
override def allTables: List[TableQuery[Table[_]]] =
|
||||
List(rValueTable, eventTable, eventOutcomeTable)
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package org.bitcoins.dlc.oracle.storage
|
||||
|
||||
import org.bitcoins.commons.jsonmodels.dlc.SigningVersion
|
||||
import org.bitcoins.crypto.{FieldElement, SchnorrNonce}
|
||||
import org.bitcoins.db.{AppConfig, CRUD, DbCommonsColumnMappers, SlickUtil}
|
||||
import slick.lifted.{ForeignKeyQuery, ProvenShape}
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
case class EventDAO()(implicit
|
||||
val ec: ExecutionContext,
|
||||
override val appConfig: AppConfig)
|
||||
extends CRUD[EventDb, SchnorrNonce]
|
||||
with SlickUtil[EventDb, SchnorrNonce] {
|
||||
|
||||
import profile.api._
|
||||
|
||||
private val mappers = new DbCommonsColumnMappers(profile)
|
||||
|
||||
import mappers._
|
||||
|
||||
override val table: TableQuery[EventTable] = TableQuery[EventTable]
|
||||
|
||||
private lazy val rValueTable: TableQuery[RValueDAO#RValueTable] =
|
||||
RValueDAO().table
|
||||
|
||||
override def createAll(ts: Vector[EventDb]): Future[Vector[EventDb]] =
|
||||
createAllNoAutoInc(ts, safeDatabase)
|
||||
|
||||
override protected def findByPrimaryKeys(
|
||||
ids: Vector[SchnorrNonce]): Query[EventTable, EventDb, Seq] =
|
||||
table.filter(_.nonce.inSet(ids))
|
||||
|
||||
override protected def findAll(
|
||||
ts: Vector[EventDb]): Query[EventTable, EventDb, Seq] =
|
||||
findByPrimaryKeys(ts.map(_.nonce))
|
||||
|
||||
def getPendingEvents: Future[Vector[EventDb]] = {
|
||||
findAll().map(_.filter(_.attestationOpt.isEmpty))
|
||||
}
|
||||
|
||||
class EventTable(tag: Tag) extends Table[EventDb](tag, schemaName, "events") {
|
||||
|
||||
def nonce: Rep[SchnorrNonce] = column("nonce", O.PrimaryKey)
|
||||
|
||||
def label: Rep[String] = column("label", O.Unique)
|
||||
|
||||
def numOutcomes: Rep[Long] = column("num_outcomes")
|
||||
|
||||
def signingVersion: Rep[SigningVersion] = column("signing_version")
|
||||
|
||||
def attestationOpt: Rep[Option[FieldElement]] = column("attestation")
|
||||
|
||||
def * : ProvenShape[EventDb] =
|
||||
(nonce,
|
||||
label,
|
||||
numOutcomes,
|
||||
signingVersion,
|
||||
attestationOpt) <> (EventDb.tupled, EventDb.unapply)
|
||||
|
||||
def fk: ForeignKeyQuery[_, RValueDb] = {
|
||||
foreignKey("fk_nonce",
|
||||
sourceColumns = nonce,
|
||||
targetTableQuery = rValueTable)(_.nonce)
|
||||
}
|
||||
|
||||
def fkLabel: ForeignKeyQuery[_, RValueDb] = {
|
||||
foreignKey("fk_label",
|
||||
sourceColumns = label,
|
||||
targetTableQuery = rValueTable)(_.label)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package org.bitcoins.dlc.oracle.storage
|
||||
|
||||
import org.bitcoins.commons.jsonmodels.dlc.SigningVersion
|
||||
import org.bitcoins.crypto.{FieldElement, SchnorrDigitalSignature, SchnorrNonce}
|
||||
|
||||
case class EventDb(
|
||||
nonce: SchnorrNonce,
|
||||
label: String,
|
||||
numOutcomes: Long,
|
||||
signingVersion: SigningVersion,
|
||||
attestationOpt: Option[FieldElement]) {
|
||||
|
||||
lazy val sigOpt: Option[SchnorrDigitalSignature] =
|
||||
attestationOpt.map(SchnorrDigitalSignature(nonce, _))
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
package org.bitcoins.dlc.oracle.storage
|
||||
|
||||
import org.bitcoins.crypto.{SchnorrNonce, Sha256Digest}
|
||||
import org.bitcoins.db.{AppConfig, CRUD, DbCommonsColumnMappers, SlickUtil}
|
||||
import slick.lifted.{ForeignKeyQuery, ProvenShape}
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
case class EventOutcomeDAO()(implicit
|
||||
val ec: ExecutionContext,
|
||||
override val appConfig: AppConfig)
|
||||
extends CRUD[EventOutcomeDb, (SchnorrNonce, String)]
|
||||
with SlickUtil[EventOutcomeDb, (SchnorrNonce, String)] {
|
||||
|
||||
import profile.api._
|
||||
|
||||
private val mappers = new DbCommonsColumnMappers(profile)
|
||||
|
||||
import mappers._
|
||||
|
||||
override val table: TableQuery[EventOutcomeTable] =
|
||||
TableQuery[EventOutcomeTable]
|
||||
|
||||
private lazy val eventTable: TableQuery[EventDAO#EventTable] =
|
||||
EventDAO().table
|
||||
|
||||
override def createAll(
|
||||
ts: Vector[EventOutcomeDb]): Future[Vector[EventOutcomeDb]] =
|
||||
createAllNoAutoInc(ts, safeDatabase)
|
||||
|
||||
override protected def findByPrimaryKeys(ids: Vector[
|
||||
(SchnorrNonce, String)]): Query[EventOutcomeTable, EventOutcomeDb, Seq] =
|
||||
table
|
||||
.filter(_.nonce.inSet(ids.map(_._1)))
|
||||
.filter(_.message.inSet(ids.map(_._2)))
|
||||
|
||||
override protected def findAll(ts: Vector[EventOutcomeDb]): Query[
|
||||
EventOutcomeTable,
|
||||
EventOutcomeDb,
|
||||
Seq] = {
|
||||
val ids = ts.map(t => (t.nonce, t.message))
|
||||
findByPrimaryKeys(ids)
|
||||
}
|
||||
|
||||
def findByNonce(nonce: SchnorrNonce): Future[Vector[EventOutcomeDb]] = {
|
||||
val query = table.filter(_.nonce === nonce)
|
||||
|
||||
safeDatabase.runVec(query.result.transactionally)
|
||||
}
|
||||
|
||||
class EventOutcomeTable(tag: Tag)
|
||||
extends Table[EventOutcomeDb](tag, schemaName, "event_outcomes") {
|
||||
|
||||
def nonce: Rep[SchnorrNonce] = column("nonce")
|
||||
|
||||
def message: Rep[String] = column("message")
|
||||
|
||||
def hashedMessage: Rep[Sha256Digest] = column("hashed_message")
|
||||
|
||||
def * : ProvenShape[EventOutcomeDb] =
|
||||
(nonce,
|
||||
message,
|
||||
hashedMessage) <> (EventOutcomeDb.tupled, EventOutcomeDb.unapply)
|
||||
|
||||
def fk: ForeignKeyQuery[_, EventDb] = {
|
||||
foreignKey("fk_nonce",
|
||||
sourceColumns = nonce,
|
||||
targetTableQuery = eventTable)(_.nonce)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package org.bitcoins.dlc.oracle.storage
|
||||
|
||||
import org.bitcoins.crypto.{SchnorrNonce, Sha256Digest}
|
||||
|
||||
case class EventOutcomeDb(
|
||||
nonce: SchnorrNonce,
|
||||
message: String,
|
||||
hashedMessage: Sha256Digest)
|
@ -0,0 +1,71 @@
|
||||
package org.bitcoins.dlc.oracle.storage
|
||||
|
||||
import org.bitcoins.core.hd.{HDCoinType, HDPurpose}
|
||||
import org.bitcoins.crypto.{SchnorrDigitalSignature, SchnorrNonce}
|
||||
import org.bitcoins.db.{AppConfig, CRUD, DbCommonsColumnMappers, SlickUtil}
|
||||
import slick.lifted.ProvenShape
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
case class RValueDAO()(implicit
|
||||
val ec: ExecutionContext,
|
||||
override val appConfig: AppConfig)
|
||||
extends CRUD[RValueDb, SchnorrNonce]
|
||||
with SlickUtil[RValueDb, SchnorrNonce] {
|
||||
|
||||
import profile.api._
|
||||
|
||||
private val mappers = new DbCommonsColumnMappers(profile)
|
||||
|
||||
import mappers._
|
||||
|
||||
override val table: TableQuery[RValueTable] = TableQuery[RValueTable]
|
||||
|
||||
override def createAll(ts: Vector[RValueDb]): Future[Vector[RValueDb]] =
|
||||
createAllNoAutoInc(ts, safeDatabase)
|
||||
|
||||
override protected def findByPrimaryKeys(
|
||||
ids: Vector[SchnorrNonce]): Query[RValueTable, RValueDb, Seq] =
|
||||
table.filter(_.nonce.inSet(ids))
|
||||
|
||||
override protected def findAll(
|
||||
ts: Vector[RValueDb]): Query[RValueTable, RValueDb, Seq] =
|
||||
findByPrimaryKeys(ts.map(_.nonce))
|
||||
|
||||
def maxKeyIndex: Future[Option[Int]] = {
|
||||
val query = table.map(_.keyIndex).max
|
||||
|
||||
safeDatabase.run(query.result.transactionally)
|
||||
}
|
||||
|
||||
class RValueTable(tag: Tag)
|
||||
extends Table[RValueDb](tag, schemaName, "r_values") {
|
||||
|
||||
def nonce: Rep[SchnorrNonce] = column("nonce", O.PrimaryKey)
|
||||
|
||||
def label: Rep[String] = column("label", O.Unique)
|
||||
|
||||
def purpose: Rep[HDPurpose] = column("hd_purpose")
|
||||
|
||||
def coinType: Rep[HDCoinType] = column("coin")
|
||||
|
||||
def accountIndex: Rep[Int] = column("account_index")
|
||||
|
||||
def chainType: Rep[Int] = column("chain_type")
|
||||
|
||||
def keyIndex: Rep[Int] = column("key_index")
|
||||
|
||||
def commitmentSignature: Rep[SchnorrDigitalSignature] =
|
||||
column("commitment_signature")
|
||||
|
||||
def * : ProvenShape[RValueDb] =
|
||||
(nonce,
|
||||
label,
|
||||
purpose,
|
||||
coinType,
|
||||
accountIndex,
|
||||
chainType,
|
||||
keyIndex,
|
||||
commitmentSignature) <> (RValueDb.tupled, RValueDb.unapply)
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package org.bitcoins.dlc.oracle.storage
|
||||
|
||||
import org.bitcoins.core.hd._
|
||||
import org.bitcoins.crypto.{SchnorrDigitalSignature, SchnorrNonce}
|
||||
|
||||
case class RValueDb(
|
||||
nonce: SchnorrNonce,
|
||||
label: String,
|
||||
purpose: HDPurpose,
|
||||
accountCoin: HDCoinType,
|
||||
accountIndex: Int,
|
||||
chainType: Int,
|
||||
keyIndex: Int,
|
||||
commitmentSignature: SchnorrDigitalSignature) {
|
||||
|
||||
val path: BIP32Path = BIP32Path.fromString(
|
||||
s"m/${purpose.constant}'/${accountCoin.toInt}'/$accountIndex'/$chainType'/$keyIndex'")
|
||||
}
|
||||
|
||||
object RValueDbHelper {
|
||||
|
||||
def apply(
|
||||
nonce: SchnorrNonce,
|
||||
label: String,
|
||||
account: HDAccount,
|
||||
chainType: Int,
|
||||
keyIndex: Int,
|
||||
commitmentSignature: SchnorrDigitalSignature): RValueDb = {
|
||||
RValueDb(nonce,
|
||||
label,
|
||||
account.purpose,
|
||||
account.coin.coinType,
|
||||
account.index,
|
||||
chainType,
|
||||
keyIndex,
|
||||
commitmentSignature)
|
||||
}
|
||||
}
|
@ -3,6 +3,8 @@ package org.bitcoins.keymanager
|
||||
import java.nio.file.{Files, Path}
|
||||
import java.util.NoSuchElementException
|
||||
|
||||
import org.bitcoins.core.crypto.BIP39Seed
|
||||
import org.bitcoins.core.crypto.ExtKeyVersion.SegWitMainNetPriv
|
||||
import org.bitcoins.core.util.TimeUtil
|
||||
import org.bitcoins.crypto.AesPassword
|
||||
import org.bitcoins.keymanager.ReadMnemonicError.{
|
||||
@ -197,4 +199,29 @@ class WalletStorageTest extends BitcoinSWalletTest with BeforeAndAfterEach {
|
||||
}
|
||||
}
|
||||
|
||||
it must "write and read an ExtPrivateKey from disk" in {
|
||||
walletConf: WalletAppConfig =>
|
||||
assert(!walletConf.seedExists())
|
||||
|
||||
val password = getBIP39PasswordOpt().getOrElse(BIP39Seed.EMPTY_PASSWORD)
|
||||
val keyVersion = SegWitMainNetPriv
|
||||
|
||||
val writtenMnemonic = getAndWriteMnemonic(walletConf)
|
||||
val expected = BIP39Seed
|
||||
.fromMnemonic(mnemonic = writtenMnemonic.mnemonicCode,
|
||||
password = password)
|
||||
.toExtPrivateKey(keyVersion)
|
||||
.toHardened
|
||||
|
||||
// should have been written by now
|
||||
assert(walletConf.seedExists())
|
||||
val seedPath = getSeedPath(walletConf)
|
||||
val read =
|
||||
WalletStorage.getPrivateKeyFromDisk(seedPath,
|
||||
keyVersion,
|
||||
passphrase,
|
||||
Some(password))
|
||||
|
||||
assert(read == expected)
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import java.time.Instant
|
||||
import java.util.NoSuchElementException
|
||||
|
||||
import org.bitcoins.core.compat._
|
||||
import org.bitcoins.core.crypto._
|
||||
import org.bitcoins.crypto.{AesEncryptedData, AesIV, AesPassword, AesSalt}
|
||||
import org.slf4j.LoggerFactory
|
||||
import scodec.bits.ByteVector
|
||||
@ -194,6 +195,26 @@ object WalletStorage {
|
||||
case CompatRight(value) => Right(value)
|
||||
}
|
||||
}
|
||||
|
||||
def getPrivateKeyFromDisk(
|
||||
seedPath: Path,
|
||||
privKeyVersion: ExtKeyPrivVersion,
|
||||
passphrase: AesPassword,
|
||||
bip39PasswordOpt: Option[String]): ExtPrivateKeyHardened = {
|
||||
val mnemonicCode = decryptMnemonicFromDisk(seedPath, passphrase) match {
|
||||
case Left(error) => sys.error(error.toString)
|
||||
case Right(mnemonic) => mnemonic.mnemonicCode
|
||||
}
|
||||
val seed = bip39PasswordOpt match {
|
||||
case Some(pw) =>
|
||||
BIP39Seed.fromMnemonic(mnemonic = mnemonicCode, password = pw)
|
||||
case None =>
|
||||
BIP39Seed.fromMnemonic(mnemonic = mnemonicCode,
|
||||
password = BIP39Seed.EMPTY_PASSWORD)
|
||||
}
|
||||
|
||||
seed.toExtPrivateKey(privKeyVersion).toHardened
|
||||
}
|
||||
}
|
||||
|
||||
sealed trait ReadMnemonicError { self: Error => }
|
||||
|
@ -398,4 +398,15 @@ object Deps {
|
||||
Test.akkaTestkit
|
||||
)
|
||||
|
||||
val dlcOracle =
|
||||
List(
|
||||
Compile.newMicroJson,
|
||||
Compile.logback
|
||||
)
|
||||
|
||||
val dlcOracleTest =
|
||||
List(
|
||||
Compile.newMicroJson,
|
||||
Compile.logback
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user