diff --git a/src/main/scala/encry/Starter.scala b/src/main/scala/encry/Starter.scala index a7b8249851..61792c9f15 100644 --- a/src/main/scala/encry/Starter.scala +++ b/src/main/scala/encry/Starter.scala @@ -1,6 +1,8 @@ package encry +import java.io.File import java.net.InetSocketAddress +import java.nio.file.Files import akka.actor.{ Actor, ActorRef } import akka.http.scaladsl.Http import cats.Functor @@ -12,8 +14,8 @@ import cats.syntax.either._ import cats.syntax.option._ import cats.syntax.validated._ import encry.Starter.InitNodeResult -import encry.api.http.DataHolderForApi import encry.api.http.DataHolderForApi.PassForStorage +import encry.api.http.{ AccStorage, DataHolderForApi, SettingsStorage } import encry.cli.ConsoleListener import encry.cli.ConsoleListener.{ prompt, StartListening } import encry.local.miner.Miner @@ -65,7 +67,10 @@ class Starter(settings: EncryAppSettings, self ! res } - def startNonEmptyNode: Either[Throwable, InitNodeResult] = + def startNonEmptyNode: Either[Throwable, InitNodeResult] = { + val storage = SettingsStorage.init(settings) + val newSettings: EncryAppSettings = storage.getSettings.getOrElse(settings) + storage.close() for { walletPassword <- { println("Please, enter wallet password:") @@ -75,16 +80,17 @@ class Starter(settings: EncryAppSettings, InitNodeResult( "", walletPassword, - settings.node.offlineGeneration, + newSettings.node.offlineGeneration, fastSync = false, - settings.snapshotSettings.enableSnapshotCreation, - settings.network.knownPeers, - settings.network.connectOnlyWithKnownPeers.getOrElse(false), + newSettings.snapshotSettings.enableSnapshotCreation, + newSettings.network.knownPeers, + newSettings.network.connectOnlyWithKnownPeers.getOrElse(false), "", - settings.network.nodeName.getOrElse(""), - settings.network.declaredAddress, - settings.network.bindAddress + newSettings.network.nodeName.getOrElse(""), + newSettings.network.declaredAddress, + newSettings.network.bindAddress ) + } def startEmptyNode: Either[Throwable, InitNodeResult] = for { @@ -141,8 +147,21 @@ class Starter(settings: EncryAppSettings, .fold(handleError, handleResult) } - def validatePassword(password: String): Validated[NonEmptyChain[String], String] = - if (password.nonEmpty) password.validNec else "Password is empty".invalidNec + def validatePassword(password: String): Validated[NonEmptyChain[String], String] = { + val passExists = Files.exists(new File(s"${settings.directory}/userKeys").toPath) + if (password.nonEmpty && !passExists) password.validNec + else if (password.nonEmpty && passExists) { + val storage = AccStorage.init(settings) + if (storage.verifyPassword(password)) { + storage.close() + password.validNec + } else { + storage.close() + "Incorrect password, please try again.".invalidNec + } + } + else "You can't use empty password, please think of another one.".invalidNec + } def validateNodeName(nodeName: String): Validated[NonEmptyChain[String], String] = if (nodeName.nonEmpty) nodeName.validNec else "Node name is empty".invalidNec @@ -380,9 +399,11 @@ class Starter(settings: EncryAppSettings, bindAddr) => import scala.concurrent.duration._ Functor[Option].compose[Future].map(initHttpApiServer)(_.terminate(3.seconds)) + val storage = AccStorage.init(settings) + storage.setPassword(password) + storage.close() if (mnemonic.nonEmpty) AccountManager.init(mnemonic, password, settings) - val walletSettings: Option[WalletSettings] = settings.wallet.map(_.copy(password = password)) - val nodeSettings: NodeSettings = settings.node.copy(offlineGeneration = offlineGeneration) + val nodeSettings: NodeSettings = settings.node.copy(offlineGeneration = offlineGeneration) val networkSettings: NetworkSettings = settings.network.copy(knownPeers = peers, connectOnlyWithKnownPeers = connectWithOnlyKnownPeers.some, @@ -395,11 +416,15 @@ class Starter(settings: EncryAppSettings, enableSnapshotCreation = snapshotCreation ) val newSettings = settings.copy( - wallet = walletSettings, node = nodeSettings, network = networkSettings, snapshotSettings = snapshotSettings ) + if (!Files.exists(new File(s"${settings.directory}/state").toPath)) { + val storage: SettingsStorage = SettingsStorage.init(newSettings) + storage.putSettings(newSettings) + storage.close() + } val influxRef: Option[ActorRef] = newSettings.influxDB.map { influxSettings => context.system .actorOf(StatsSender.props(influxSettings, newSettings.network, newSettings.constants), "statsSender") diff --git a/src/main/scala/encry/api/http/AccStorage.scala b/src/main/scala/encry/api/http/AccStorage.scala index 92c138194c..eda6a852e4 100644 --- a/src/main/scala/encry/api/http/AccStorage.scala +++ b/src/main/scala/encry/api/http/AccStorage.scala @@ -51,7 +51,11 @@ object AccStorage extends StrictLogging { val PasswordHashKey: StorageKey = StorageKey @@ Algos.hash("Password_Key") val SaltKey: StorageKey = StorageKey @@ Algos.hash("Salt_Key") - def getDirStorage(settings: EncryAppSettings): File = new File(s"${settings.directory}/userKeys") + def getDirStorage(settings: EncryAppSettings): File = { + val dir = new File(s"${settings.directory}/userKeys") + dir.mkdirs() + dir + } def init(settings: EncryAppSettings): AccStorage = new AccStorage { override val storage: DB = LevelDbFactory.factory.open(getDirStorage(settings), new Options) diff --git a/src/main/scala/encry/api/http/SettingsStorage.scala b/src/main/scala/encry/api/http/SettingsStorage.scala new file mode 100644 index 0000000000..b310927233 --- /dev/null +++ b/src/main/scala/encry/api/http/SettingsStorage.scala @@ -0,0 +1,79 @@ +package encry.api.http + +import java.io.{ByteArrayInputStream, ByteArrayOutputStream, File, ObjectInputStream, ObjectOutputStream} +import com.typesafe.scalalogging.StrictLogging +import encry.settings.EncryAppSettings +import org.encryfoundation.common.utils.Algos +import org.iq80.leveldb.{DB, Options} +import scorex.crypto.hash.Digest32 +import encry.storage.levelDb.versionalLevelDB.LevelDbFactory + +trait SettingsStorage extends StrictLogging with AutoCloseable { + + val storage: DB + + val SettingsKey: Digest32 = Algos.hash(s"Settings_Key") + + def getSettings: Option[EncryAppSettings] = deserialise(storage.get(SettingsKey)) + + def putSettings(settings: EncryAppSettings): Unit = { + val batch = storage.createWriteBatch() + try { + val serialisedSettings: Array[Byte] = serialise(settings) + batch.put(SettingsKey, serialisedSettings) + storage.write(batch) + } catch { + case err: Throwable => throw new Exception(s"Error during saving settings cause of $err") + } finally { + batch.close() + } + } + + private final def serialise(value: EncryAppSettings): Array[Byte] = { + val stream: ByteArrayOutputStream = new ByteArrayOutputStream() + val oos = new ObjectOutputStream(stream) + try { + oos.writeObject(value) + stream.toByteArray + } catch { + case err: Throwable => throw new Exception(s"Error during serialisation cause of $err") + } finally { + oos.close() + } + } + + private final def deserialise(bytes: Array[Byte]): Option[EncryAppSettings] = { + val ois = new ObjectInputStream(new ByteArrayInputStream(bytes)) + try { + val value = ois.readObject + val deserialised: Option[EncryAppSettings] = value match { + case set: EncryAppSettings => + logger.info(s"Deserialisation ended successfully") + Some(set) + } + deserialised + } catch { + case err: Throwable => throw new Exception(s"Error during serialisation cause of $err") + } finally { + ois.close() + } + } + + override def close(): Unit = storage.close() + +} + +object SettingsStorage extends StrictLogging { + + def getDirStorage(settings: EncryAppSettings): File = { + val dir = new File(s"${settings.directory}/userSettings") + dir.mkdirs() + dir + } + + + def init(settings: EncryAppSettings): SettingsStorage = new SettingsStorage { + override val storage: DB = LevelDbFactory.factory.open(getDirStorage(settings), new Options) + } + +}