Remove CompatEither, it was needed for historical purposes to support… (#2394)

* Remove CompatEither, it was needed for historical purposes to support Scala 2.11.x

* Revert files from another change
This commit is contained in:
Chris Stewart 2020-12-18 09:51:28 -06:00 committed by GitHub
parent 2db21fc3d2
commit 719fab23b7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 70 additions and 272 deletions

View file

@ -1,53 +0,0 @@
package org.bitcoins.core.compat
import org.bitcoins.testkit.util.BitcoinSUnitTest
import scala.annotation.nowarn
import scala.util.{Failure, Success}
class CompatEitherTest extends BitcoinSUnitTest {
it should "create left and right" in {
val right = Right("Right")
val compatRight = CompatEither(right)
assert(compatRight.isInstanceOf[CompatRight[Nothing, String]])
assert(compatRight.toTry == Success("Right"))
val exception = new RuntimeException("Left")
val left = Left(exception)
val compatLeft = CompatEither(left)
assert(compatLeft.isInstanceOf[CompatLeft[RuntimeException, Nothing]])
assert(compatLeft.toTry == Failure(exception))
}
it should "do traverse operations" in {
val mappedRight = CompatEither(Right(12)).map(_ => "flower")
assert(mappedRight == CompatEither(Right("flower")))
@nowarn val mappedLeft = CompatEither(Left(12)).map(_ => "flower")
assert(mappedLeft == CompatEither(Left(12)))
val flatmappedRight: CompatEither[Int, String] =
CompatEither(Right(12)).flatMap(_ => CompatEither(Right("flower")))
assert(flatmappedRight == CompatRight("flower"))
@nowarn val flatmappedLeft =
CompatEither(Left(12)).flatMap(_ => CompatEither(Left("21")))
assert(flatmappedLeft == CompatLeft(12))
@nowarn val foldedRight = CompatEither(Right(12)).fold({ _ =>
"left"
},
{ _ =>
"right"
})
assert(foldedRight == "right")
@nowarn val foldedLeft = CompatEither(Left(12)).fold({ _ =>
"left"
},
{ _ =>
"right"
})
assert(foldedLeft == "left")
}
}

View file

@ -1,89 +0,0 @@
package org.bitcoins.core.compat
import scala.util.{Failure, Success, Try}
/** This is an implementation of (parts of)
* `scala.util.Either`, compatible with Scala 2.11,
* 2.12 and 2.13. It is in large parts cribbed from
* the Scala 2.12 standard library.
*/
sealed private[bitcoins] trait CompatEither[+A, +B] {
protected val underlying: Either[A, B]
/** The given function is applied if this is a `Right`.
*
* {{{
* Right(12).map(x => "flower") // Result: Right("flower")
* Left(12).map(x => "flower") // Result: Left(12)
* }}}
*/
def map[B1](f: B => B1): CompatEither[A, B1] =
underlying match {
case Right(b) => CompatRight(f(b))
case _ => this.asInstanceOf[CompatEither[A, B1]]
}
/** Binds the given function across `Right`.
*
* @param f The function to bind across `Right`.
*/
def flatMap[A1 >: A, B1](f: B => CompatEither[A1, B1]): CompatEither[A1, B1] =
underlying match {
case Right(b) =>
f(b) match {
case CompatLeft(value) => CompatLeft(value)
case CompatRight(value) => CompatRight(value)
}
case Left(l) => CompatLeft(l)
}
def toTry(implicit ev: A <:< Throwable): Try[B] =
underlying match {
case Right(b) => Success(b)
case Left(a) => Failure(a)
}
/** Applies `fa` if this is a `Left` or `fb` if this is a `Right`.
*
* @example {{{
* val result = util.Try("42".toInt).toEither
* result.fold(
* e => s"Operation failed with $e",
* v => s"Operation produced value: $v"
* )
* }}}
*
* @param fa the function to apply if this is a `Left`
* @param fb the function to apply if this is a `Right`
* @return the results of applying the function
*/
def fold[C](fa: A => C, fb: B => C): C =
underlying match {
case Right(b) => fb(b)
case Left(a) => fa(a)
}
}
object CompatEither {
/** Converts the given `scala.util.Either` to a `CompatEither` */
def apply[A, B](either: Either[A, B]): CompatEither[A, B] =
either match {
case Left(value) => CompatLeft(value)
case Right(value) => CompatRight(value)
}
}
/** Analogous to `scala.util.Left` */
case class CompatLeft[A, B](value: A) extends CompatEither[A, B] {
val underlying = scala.util.Left[A, B](value)
}
/** Analogous to `scala.util.Right` */
case class CompatRight[A, B](value: B) extends CompatEither[A, B] {
val underlying = scala.util.Right[A, B](value)
}

View file

@ -1,50 +0,0 @@
package org.bitcoins.core.util
import org.bitcoins.core.compat.{CompatEither, CompatLeft, CompatRight}
import scala.concurrent.{ExecutionContext, Future}
/**
* @define liftBiasedFut Given a [[scala.Either Either]] that contains a
* [[scala.concurrent.Future Future[L | R] ]] only on one side,
* transforms it into a future [[scala.Either Either[L, R] ]]
*/
object EitherUtil {
/**
* Flattens a nested `Either[Foo, Future[Foo, Bar]]` into
* a `Future[Either[Foo, Bar]]`. This is EitherUtiluseful for situtations
* where the right hand side of an either is asynchronous.
*/
def flattenFutureE[L, R](
either: CompatEither[L, Future[CompatEither[L, R]]]
): Future[CompatEither[L, R]] = {
def ifLeft(left: L): Future[CompatEither[L, R]] =
Future.successful(CompatLeft(left))
def ifRight(
rightF: Future[CompatEither[L, R]]): Future[CompatEither[L, R]] =
rightF
either.fold(ifLeft, ifRight)
}
/** $liftBiasedFut */
def liftRightBiasedFutureE[L, R](
either: CompatEither[L, Future[R]]
)(implicit ec: ExecutionContext): Future[CompatEither[L, R]] =
either match {
case CompatRight(fut) => fut.map(elem => CompatRight(elem))
case CompatLeft(l) => Future.successful(CompatLeft(l))
}
/** $liftBiasedFut */
def listLeftBiasedFutureE[L, R](
either: Either[Future[L], R]
)(implicit ec: ExecutionContext): Future[Either[L, R]] =
either match {
case Left(fut) => fut.map(Left(_))
case Right(l) => Future.successful(Right(l))
}
}

View file

@ -1,6 +1,5 @@
package org.bitcoins.keymanager
import org.bitcoins.core.compat.CompatEither
import org.bitcoins.core.crypto._
import org.bitcoins.crypto.{AesCrypt, AesEncryptedData, AesPassword, AesSalt}
import scodec.bits.ByteVector
@ -48,7 +47,7 @@ case class EncryptedSeed(
private def decryptStr(password: AesPassword): Try[String] = {
val key = password.toKey(salt)
val either = AesCrypt.decrypt(value, key)
CompatEither(either).toTry.flatMap { decrypted =>
either.toTry.flatMap { decrypted =>
decrypted.decodeUtf8 match {
case Left(_) =>
// when failing to decode this to a UTF-8 string

View file

@ -1,6 +1,5 @@
package org.bitcoins.keymanager
import org.bitcoins.core.compat._
import org.bitcoins.core.crypto._
import org.bitcoins.crypto._
import scodec.bits.ByteVector
@ -156,22 +155,22 @@ object WalletStorage extends KeyManagerLogger {
}
private def readJsonFromDisk(
seedPath: Path): CompatEither[ReadMnemonicError, Value] = {
seedPath: Path): Either[ReadMnemonicError, Value] = {
if (Files.isRegularFile(seedPath)) {
val rawJson = Files.readAllLines(seedPath).asScala.mkString("\n")
logger.debug(s"Read raw mnemonic from $seedPath")
Try(ujson.read(rawJson)) match {
case Failure(ujson.ParseException(clue, _, _, _)) =>
CompatLeft(ReadMnemonicError.JsonParsingError(clue))
Left(ReadMnemonicError.JsonParsingError(clue))
case Failure(exception) => throw exception
case Success(value) =>
logger.debug(s"Parsed $seedPath into valid json")
CompatRight(value)
Right(value)
}
} else {
logger.error(s"Mnemonic not found at $seedPath")
CompatLeft(ReadMnemonicError.NotFoundError)
Left(ReadMnemonicError.NotFoundError)
}
}
@ -179,14 +178,14 @@ object WalletStorage extends KeyManagerLogger {
* performing no decryption
*/
private def readEncryptedMnemonicFromDisk(
seedPath: Path): CompatEither[ReadMnemonicError, EncryptedSeed] = {
seedPath: Path): Either[ReadMnemonicError, EncryptedSeed] = {
val jsonE = readJsonFromDisk(seedPath)
import MnemonicJsonKeys._
import ReadMnemonicError._
val readJsonTupleEither: CompatEither[
val readJsonTupleEither: Either[
ReadMnemonicError,
(String, String, String, Long)] = jsonE.flatMap { json =>
logger.trace(s"Read encrypted mnemonic JSON: $json")
@ -197,13 +196,13 @@ object WalletStorage extends KeyManagerLogger {
val rawSaltString = json(SALT).str
(ivString, cipherTextString, rawSaltString, creationTimeNum)
} match {
case Success(value) => CompatRight(value)
case Success(value) => Right(value)
case Failure(exception) =>
CompatLeft(JsonParsingError(exception.getMessage))
Left(JsonParsingError(exception.getMessage))
}
}
val encryptedEither: CompatEither[ReadMnemonicError, EncryptedSeed] =
val encryptedEither: Either[ReadMnemonicError, EncryptedSeed] =
readJsonTupleEither.flatMap {
case (rawIv, rawCipherText, rawSalt, rawCreationTime) =>
val encryptedOpt = for {
@ -217,39 +216,38 @@ object WalletStorage extends KeyManagerLogger {
salt,
Instant.ofEpochSecond(rawCreationTime))
}
val toRight: Option[CompatRight[ReadMnemonicError, EncryptedSeed]] =
val toRight: Option[Right[ReadMnemonicError, EncryptedSeed]] =
encryptedOpt
.map(CompatRight(_))
.map(Right(_))
toRight.getOrElse(
CompatLeft(JsonParsingError("JSON contents was not hex strings")))
Left(JsonParsingError("JSON contents was not hex strings")))
}
encryptedEither
}
/** Reads the raw unencrypted mnemonic from disk */
private def readUnencryptedMnemonicFromDisk(
seedPath: Path): CompatEither[ReadMnemonicError, DecryptedMnemonic] = {
seedPath: Path): Either[ReadMnemonicError, DecryptedMnemonic] = {
val jsonE = readJsonFromDisk(seedPath)
import MnemonicJsonKeys._
import ReadMnemonicError._
val readJsonTupleEither: CompatEither[
ReadMnemonicError,
(Vector[String], Long)] = jsonE.flatMap { json =>
logger.trace(s"Read mnemonic JSON: Masked(json)")
Try {
val creationTimeNum = parseCreationTime(json)
val words = json(MNEMONIC_SEED).arr.toVector.map(_.str)
(words, creationTimeNum)
} match {
case Success(value) => CompatRight(value)
case Failure(exception) =>
CompatLeft(JsonParsingError(exception.getMessage))
val readJsonTupleEither: Either[ReadMnemonicError, (Vector[String], Long)] =
jsonE.flatMap { json =>
logger.trace(s"Read mnemonic JSON: Masked(json)")
Try {
val creationTimeNum = parseCreationTime(json)
val words = json(MNEMONIC_SEED).arr.toVector.map(_.str)
(words, creationTimeNum)
} match {
case Success(value) => Right(value)
case Failure(exception) =>
Left(JsonParsingError(exception.getMessage))
}
}
}
readJsonTupleEither.flatMap {
case (words, rawCreationTime) =>
@ -261,25 +259,25 @@ object WalletStorage extends KeyManagerLogger {
Instant.ofEpochSecond(rawCreationTime))
}
val toRight: Try[CompatRight[ReadMnemonicError, DecryptedMnemonic]] =
val toRight: Try[Right[ReadMnemonicError, DecryptedMnemonic]] =
decryptedMnemonicT
.map(CompatRight(_))
.map(Right(_))
toRight.getOrElse(
CompatLeft(JsonParsingError("JSON contents was correctly formatted")))
Left(JsonParsingError("JSON contents was correctly formatted")))
}
}
/** Reads the raw unencrypted xprv from disk */
private def readUnencryptedSeedFromDisk(
seedPath: Path): CompatEither[ReadMnemonicError, DecryptedExtPrivKey] = {
seedPath: Path): Either[ReadMnemonicError, DecryptedExtPrivKey] = {
val jsonE = readJsonFromDisk(seedPath)
import MnemonicJsonKeys._
import ReadMnemonicError._
val readJsonTupleEither: CompatEither[ReadMnemonicError, (String, Long)] =
val readJsonTupleEither: Either[ReadMnemonicError, (String, Long)] =
jsonE.flatMap { json =>
logger.trace(s"Read mnemonic JSON: Masked(json)")
Try {
@ -287,9 +285,9 @@ object WalletStorage extends KeyManagerLogger {
val xprvStr = json(XPRV).str
(xprvStr, creationTimeNum)
} match {
case Success(value) => CompatRight(value)
case Success(value) => Right(value)
case Failure(exception) =>
CompatLeft(JsonParsingError(exception.getMessage))
Left(JsonParsingError(exception.getMessage))
}
}
@ -300,12 +298,12 @@ object WalletStorage extends KeyManagerLogger {
DecryptedExtPrivKey(xprv, Instant.ofEpochSecond(rawCreationTime))
}
val toRight: Try[CompatRight[ReadMnemonicError, DecryptedExtPrivKey]] =
val toRight: Try[Right[ReadMnemonicError, DecryptedExtPrivKey]] =
decryptedExtPrivKeyT
.map(CompatRight(_))
.map(Right(_))
toRight.getOrElse(
CompatLeft(JsonParsingError("JSON contents was correctly formatted")))
Left(JsonParsingError("JSON contents was correctly formatted")))
}
}
@ -318,7 +316,7 @@ object WalletStorage extends KeyManagerLogger {
passphraseOpt: Option[AesPassword]): Either[
ReadMnemonicError,
DecryptedSeedState] = {
val decryptedEither: CompatEither[ReadMnemonicError, DecryptedSeedState] =
val decryptedEither: Either[ReadMnemonicError, DecryptedSeedState] =
passphraseOpt match {
case Some(passphrase) =>
val encryptedEither = readEncryptedMnemonicFromDisk(seedPath)
@ -329,32 +327,29 @@ object WalletStorage extends KeyManagerLogger {
encrypted.toExtPrivKey(passphrase) match {
case Failure(exc) =>
logger.error(s"Error when decrypting $encrypted: $exc")
CompatLeft(ReadMnemonicError.DecryptionError)
Left(ReadMnemonicError.DecryptionError)
case Success(xprv) =>
logger.debug(s"Decrypted $encrypted successfully")
val decryptedExtPrivKey =
DecryptedExtPrivKey(xprv, encrypted.creationTime)
CompatRight(decryptedExtPrivKey)
Right(decryptedExtPrivKey)
}
case Success(mnemonic) =>
logger.debug(s"Decrypted $encrypted successfully")
val decryptedMnemonic =
DecryptedMnemonic(mnemonic, encrypted.creationTime)
CompatRight(decryptedMnemonic)
Right(decryptedMnemonic)
}
}
case None =>
readUnencryptedMnemonicFromDisk(seedPath) match {
case CompatLeft(_) =>
case Left(_) =>
readUnencryptedSeedFromDisk(seedPath)
case CompatRight(mnemonic) => CompatRight(mnemonic)
case Right(mnemonic) => Right(mnemonic)
}
}
decryptedEither match {
case CompatLeft(value) => Left(value)
case CompatRight(value) => Right(value)
}
decryptedEither
}
def changeAesPassword(

View file

@ -5,7 +5,6 @@ import org.bitcoins.core.api.keymanager.{
BIP39KeyManagerCreateApi,
KeyManagerApi
}
import org.bitcoins.core.compat.{CompatEither, CompatLeft, CompatRight}
import org.bitcoins.core.crypto._
import org.bitcoins.core.hd.{HDAccount, HDPath}
import org.bitcoins.core.util.{BitcoinSLogger, HDUtil, TimeUtil}
@ -99,26 +98,24 @@ object BIP39KeyManager
val time = TimeUtil.now
val writtenToDiskE: CompatEither[KeyManagerInitializeError, KeyManagerApi] =
val writtenToDiskE: Either[KeyManagerInitializeError, KeyManagerApi] =
if (Files.notExists(seedPath)) {
logger.info(
s"Seed path parent directory does not exist, creating ${seedPath.getParent}")
Files.createDirectories(seedPath.getParent)
val mnemonicT = Try(MnemonicCode.fromEntropy(entropy))
val mnemonicE: CompatEither[KeyManagerInitializeError, MnemonicCode] =
val mnemonicE: Either[KeyManagerInitializeError, MnemonicCode] =
mnemonicT match {
case Success(mnemonic) =>
logger.info(s"Created mnemonic from entropy")
CompatEither(Right(mnemonic))
Right(mnemonic)
case Failure(err) =>
logger.error(s"Could not create mnemonic from entropy! $err")
CompatEither(Left(InitializeKeyManagerError.BadEntropy))
Left(InitializeKeyManagerError.BadEntropy)
}
val writableMnemonicE: CompatEither[
KeyManagerInitializeError,
SeedState] =
val writableMnemonicE: Either[KeyManagerInitializeError, SeedState] =
mnemonicE.map { mnemonic =>
val decryptedMnemonic = DecryptedMnemonic(mnemonic, time)
aesPasswordOpt match {
@ -148,16 +145,16 @@ object BIP39KeyManager
WalletStorage.decryptSeedFromDisk(kmParams.seedPath,
aesPasswordOpt) match {
case Right(mnemonic: DecryptedMnemonic) =>
CompatRight(
Right(
fromMnemonic(mnemonic = mnemonic.mnemonicCode,
kmParams = kmParams,
bip39PasswordOpt = bip39PasswordOpt,
creationTime = mnemonic.creationTime))
case Right(xprv: DecryptedExtPrivKey) =>
val km = new BIP39KeyManager(xprv.xprv, kmParams, xprv.creationTime)
CompatRight(km)
Right(km)
case Left(err) =>
CompatLeft(
Left(
InitializeKeyManagerError.FailedToReadWrittenSeed(
JsonParsingError(err.toString)))
}
@ -169,7 +166,7 @@ object BIP39KeyManager
bip39PasswordOpt,
kmParams = kmParams)
val biasedFinalE: CompatEither[KeyManagerInitializeError, BIP39KeyManager] =
val biasedFinalE: Either[KeyManagerInitializeError, BIP39KeyManager] =
for {
kmBeforeWrite <- writtenToDiskE
invariant <- unlocked match {
@ -178,20 +175,20 @@ object BIP39KeyManager
unlockedKeyManager == kmBeforeWrite,
s"We could not read the key manager we just wrote! $kmBeforeWrite != $unlockedKeyManager"
)
CompatRight(unlockedKeyManager)
Right(unlockedKeyManager)
case Left(err) =>
CompatLeft(InitializeKeyManagerError.FailedToReadWrittenSeed(err))
Left(InitializeKeyManagerError.FailedToReadWrittenSeed(err))
}
} yield {
invariant
}
biasedFinalE match {
case CompatRight(initSuccess) =>
case Right(initSuccess) =>
logger.info(s"Successfully initialized wallet")
Right(initSuccess)
case CompatLeft(err) =>
case Left(err) =>
logger.error(s"Failed to initialize key manager with err=$err")
Left(err)
}

View file

@ -7,7 +7,6 @@ import org.bitcoins.core.api.wallet.{
AddUtxoResult,
AddUtxoSuccess
}
import org.bitcoins.core.compat._
import org.bitcoins.core.consensus.Consensus
import org.bitcoins.core.hd.HDAccount
import org.bitcoins.core.number.UInt32
@ -22,7 +21,7 @@ import org.bitcoins.core.protocol.transaction.{
TransactionOutPoint,
TransactionOutput
}
import org.bitcoins.core.util.{EitherUtil, FutureUtil}
import org.bitcoins.core.util.FutureUtil
import org.bitcoins.core.wallet.utxo._
import org.bitcoins.crypto.DoubleSha256DigestBE
import org.bitcoins.wallet.{Wallet, WalletLogger}
@ -164,14 +163,14 @@ private[wallet] trait UtxoHandling extends WalletLogger {
* it in our address table
*/
private def findAddress(
spk: ScriptPubKey): Future[CompatEither[AddUtxoError, AddressDb]] =
spk: ScriptPubKey): Future[Either[AddUtxoError, AddressDb]] =
BitcoinAddress.fromScriptPubKeyT(spk, networkParameters) match {
case Success(address) =>
addressDAO.findAddress(address).map {
case Some(addrDb) => CompatRight(addrDb)
case None => CompatLeft(AddUtxoError.AddressNotFound)
case Some(addrDb) => Right(addrDb)
case None => Left(AddUtxoError.AddressNotFound)
}
case Failure(_) => Future.successful(CompatLeft(AddUtxoError.BadSPK))
case Failure(_) => Future.successful(Left(AddUtxoError.BadSPK))
}
/** Constructs a DB level representation of the given UTXO, and persist it to disk */
@ -257,12 +256,13 @@ private[wallet] trait UtxoHandling extends WalletLogger {
// second check: do we have an address associated with the provided
// output in our DB?
def addressDbEitherF: Future[CompatEither[AddUtxoError, AddressDb]] =
def addressDbEitherF: Future[Either[AddUtxoError, AddressDb]] = {
findAddress(output.scriptPubKey)
}
// insert the UTXO into the DB
addressDbEitherF.flatMap { addressDbE =>
def biasedE: CompatEither[AddUtxoError, Future[SpendingInfoDb]] =
addressDbEitherF
.map { addressDbE =>
for {
addressDb <- addressDbE
} yield writeUtxo(tx = transaction,
@ -271,12 +271,11 @@ private[wallet] trait UtxoHandling extends WalletLogger {
outPoint = outPoint,
addressDb = addressDb,
blockHash = blockHash)
EitherUtil.liftRightBiasedFutureE(biasedE)
} map {
case CompatRight(utxo) => AddUtxoSuccess(utxo)
case CompatLeft(e) => e
}
}
.flatMap {
case Right(utxoF) => utxoF.map(AddUtxoSuccess(_))
case Left(e) => Future.successful(e)
}
}
}