diff --git a/casper/src/main/scala/coop/rchain/casper/CasperConf.scala b/casper/src/main/scala/coop/rchain/casper/CasperConf.scala index be7d179d633..eb77783d967 100644 --- a/casper/src/main/scala/coop/rchain/casper/CasperConf.scala +++ b/casper/src/main/scala/coop/rchain/casper/CasperConf.scala @@ -27,8 +27,8 @@ final case class CasperConf( final case class GenesisBlockData( genesisDataDir: Path, - bondsFile: Option[String], - walletsFile: Option[String], + bondsFile: String, + walletsFile: String, bondMinimum: Long, bondMaximum: Long, epochLength: Int, diff --git a/casper/src/main/scala/coop/rchain/casper/engine/ApproveBlockProtocol.scala b/casper/src/main/scala/coop/rchain/casper/engine/ApproveBlockProtocol.scala index 701a815d8a8..772c440f7e7 100644 --- a/casper/src/main/scala/coop/rchain/casper/engine/ApproveBlockProtocol.scala +++ b/casper/src/main/scala/coop/rchain/casper/engine/ApproveBlockProtocol.scala @@ -59,10 +59,10 @@ object ApproveBlockProtocol { ) def of[F[_]: Sync: Concurrent: RaiseIOError: CommUtil: Log: EventLog: Time: Metrics: RuntimeManager: LastApprovedBlock]( - maybeBondsPath: Option[String], + bondsPath: String, autogenShardSize: Int, genesisPath: Path, - maybeVaultsPath: Option[String], + vaultsPath: String, minimumBond: Long, maximumBond: Long, epochLength: Int, @@ -78,12 +78,10 @@ object ApproveBlockProtocol { now <- Time[F].currentMillis timestamp = deployTimestamp.getOrElse(now) - vaults <- VaultParser.parse[F](maybeVaultsPath, genesisPath.resolve("wallets.txt")) + vaults <- VaultParser.parse[F](vaultsPath) bonds <- BondsParser.parse[F]( - maybeBondsPath, - genesisPath.resolve("bonds.txt"), - autogenShardSize, - genesisPath + bondsPath, + autogenShardSize ) genesisBlock <- if (bonds.size <= requiredSigs) diff --git a/casper/src/main/scala/coop/rchain/casper/engine/CasperLaunch.scala b/casper/src/main/scala/coop/rchain/casper/engine/CasperLaunch.scala index b42f0b6e202..95a3cabf4ab 100644 --- a/casper/src/main/scala/coop/rchain/casper/engine/CasperLaunch.scala +++ b/casper/src/main/scala/coop/rchain/casper/engine/CasperLaunch.scala @@ -145,17 +145,11 @@ object CasperLaunch { timestamp <- conf.genesisBlockData.deployTimestamp.fold(Time[F].currentMillis)(_.pure[F]) bonds <- BondsParser.parse[F]( conf.genesisBlockData.bondsFile, - conf.genesisBlockData.genesisDataDir.resolve("bonds.txt"), - conf.genesisCeremony.autogenShardSize, - conf.genesisBlockData.genesisDataDir + conf.genesisCeremony.autogenShardSize ) validatorId <- ValidatorIdentity.fromPrivateKeyWithLogging[F](conf.validatorPrivateKey) - vaults <- VaultParser.parse( - conf.genesisBlockData.walletsFile - .map(Paths.get(_)) - .getOrElse(conf.genesisBlockData.genesisDataDir.resolve("wallets.txt")) - ) + vaults <- VaultParser.parse(conf.genesisBlockData.walletsFile) bap <- BlockApproverProtocol.of( validatorId.get, timestamp, diff --git a/casper/src/main/scala/coop/rchain/casper/util/BondsParser.scala b/casper/src/main/scala/coop/rchain/casper/util/BondsParser.scala index e49c592475d..afba585bf83 100644 --- a/casper/src/main/scala/coop/rchain/casper/util/BondsParser.scala +++ b/casper/src/main/scala/coop/rchain/casper/util/BondsParser.scala @@ -25,6 +25,16 @@ object BondsParser { SourceIO .open[F](bondsPath) .use(_.getLines) + .flatTap( + bondsStrs => + Sync[F] + .raiseError( + new Exception( + s"BONDS FILE $bondsPath IS EMPTY. Please fill it or remove so populated bonds file is created on startup." + ) + ) + .whenA(bondsStrs.isEmpty) + ) .map { lines => Try { lines @@ -40,52 +50,45 @@ object BondsParser { bonds.toList .traverse_ { case (pk, stake) => - Log[F].info(s"Parsed validator ${Base16.encode(pk.bytes)} with bond $stake") + Log[F].info(s"Bond loaded ${Base16.encode(pk.bytes)} => $stake") } .as(bonds) - case Failure(_) => - Sync[F].raiseError(new Exception(s"Bonds file $bondsPath cannot be parsed")) + case Failure(e) => + Sync[F].raiseError( + new Exception(s"FAILED PARSING BONDS FILE $bondsPath: ${e}") + ) } def parse[F[_]: Sync: Log: RaiseIOError]( - maybeBondsPath: Option[String], - defaultBondsPath: Path, - autogenShardShize: Int, - genesisPath: Path - ): F[Map[PublicKey, Long]] = - maybeBondsPath match { - case Some(bondsPathStr) => - val bondsPath = Paths.get(bondsPathStr) - exists(bondsPath).ifM( - parse(bondsPath), - Sync[F].raiseError(new Exception(s"Specified bonds file $bondsPath does not exist")) - ) - case None => - exists(defaultBondsPath).ifM( - Log[F].info(s"Using default file $defaultBondsPath") >> parse(defaultBondsPath), - Log[F].warn( - s"Bonds file was not specified and default bonds file does not exist. Falling back on generating random validators." - ) >> newValidators[F](autogenShardShize, genesisPath) - ) - } + bondsPathStr: String, + autogenShardSize: Int + ): F[Map[PublicKey, Long]] = { + val bondsPath = Paths.get(bondsPathStr) + exists(bondsPath).ifM( + Log[F].info(s"Parsing bonds file ${bondsPath}.") >> parse(bondsPath), + Log[F].warn(s"BONDS FILE NOT FOUND: ${bondsPath}. Creating file with random bonds.") >> + newValidators[F](autogenShardSize, Path.of(bondsPathStr).toAbsolutePath) + ) + } private def newValidators[F[_]: Monad: Sync: Log]( autogenShardSize: Int, - genesisPath: Path + bondsFilePath: Path ): F[Map[PublicKey, Long]] = { - val keys = Vector.fill(autogenShardSize)(Secp256k1.newKeyPair) - val (_, pubKeys) = keys.unzip - val bonds = pubKeys.zipWithIndex.toMap.mapValues(_.toLong + 1L) - val genBondsFile = genesisPath.resolve(s"bonds.txt").toFile + val genesisFolder = bondsFilePath.getParent + val keys = Vector.fill(autogenShardSize)(Secp256k1.newKeyPair) + val (_, pubKeys) = keys.unzip + val bonds = pubKeys.zipWithIndex.toMap.mapValues(_.toLong + 1L) + val genBondsFile = bondsFilePath.toFile val skFiles = - Sync[F].delay(genesisPath.toFile.mkdir()) >> + Sync[F].delay(genesisFolder.toFile.mkdirs()) >> Sync[F].delay { keys.foreach { //create files showing the secret key for each public key case (sec, pub) => val sk = Base16.encode(sec.bytes) val pk = Base16.encode(pub.bytes) - val skFile = genesisPath.resolve(s"$pk.sk").toFile + val skFile = genesisFolder.resolve(s"$pk.sk").toFile val printer = new PrintWriter(skFile) printer.println(sk) printer.close() @@ -99,7 +102,7 @@ object BondsParser { _ <- bonds.toList.traverse_ { case (pub, stake) => val pk = Base16.encode(pub.bytes) - Log[F].info(s"Created validator $pk with bond $stake") >> Sync[F].delay( + Log[F].info(s"Bond generated $pk => $stake") >> Sync[F].delay( printer.println(s"$pk $stake") ) } diff --git a/casper/src/main/scala/coop/rchain/casper/util/VaultParser.scala b/casper/src/main/scala/coop/rchain/casper/util/VaultParser.scala index 5eac506220f..a2bf050baf1 100644 --- a/casper/src/main/scala/coop/rchain/casper/util/VaultParser.scala +++ b/casper/src/main/scala/coop/rchain/casper/util/VaultParser.scala @@ -1,47 +1,30 @@ package coop.rchain.casper.util -import java.io.FileNotFoundException -import java.nio.file.{Path, Paths} - -import cats.Monad import cats.effect.Sync import cats.implicits._ import coop.rchain.blockstorage.util.io.IOError.RaiseIOError -import coop.rchain.blockstorage.util.io.{exists, FileNotFound, SourceIO} +import coop.rchain.blockstorage.util.io.{exists, SourceIO} import coop.rchain.casper.genesis.contracts.Vault import coop.rchain.rholang.interpreter.util.RevAddress import coop.rchain.shared.Log +import java.nio.file.{Path, Paths} import scala.util.{Failure, Success, Try} object VaultParser { def parse[F[_]: Sync: Log: RaiseIOError]( - maybeVaultPath: Option[String], - defaultVaultPath: Path - ): F[Seq[Vault]] = - maybeVaultPath match { - case Some(vaultsPathStr) => - val vaultsPath = Paths.get(vaultsPathStr) - exists(vaultsPath).ifM( - parse[F](vaultsPath), - RaiseIOError[F].raise( - FileNotFound( - new FileNotFoundException(s"Specified vaults file $vaultsPath does not exist") - ) - ) - ) - case None => - exists(defaultVaultPath).ifM( - Log[F].info(s"Using default file $defaultVaultPath") >> parse[F]( - defaultVaultPath - ), - Log[F] - .warn( - "No vaults file specified and no default file found. No vaults will exist at genesis." - ) - .map(_ => Seq.empty[Vault]) - ) - } + vaultsPathStr: String + ): F[Seq[Vault]] = { + val vaultsPath = Paths.get(vaultsPathStr) + exists(vaultsPath).ifM( + Log[F] + .info(s"Parsing wallets file ${vaultsPath}.") >> + parse[F](vaultsPath), + Log[F] + .warn(s"WALLETS FILE NOT FOUND: ${vaultsPath}. No vaults will be put in genesis block.") + .as(Seq.empty[Vault]) + ) + } def parse[F[_]: Sync: Log: RaiseIOError](vaultsPath: Path): F[Seq[Vault]] = for { @@ -51,17 +34,18 @@ object VaultParser { .adaptError { case ex: Throwable => new RuntimeException( - s"Failed to read ${vaultsPath.toAbsolutePath} for reason: ${ex.getMessage}" + s"FAILED TO READ ${vaultsPath.toAbsolutePath}: ${ex.getMessage}" ) } vaults <- lines.traverse(parseLine[F]) } yield vaults private def parseLine[F[_]: Sync: Log: RaiseIOError](line: String): F[Vault] = - Sync[F].fromEither( - fromLine(line) - .leftMap(errMsg => new RuntimeException(s"Error in parsing vaults file: $errMsg")) - ) + Sync[F] + .fromEither( + fromLine(line) + .leftMap(errMsg => new RuntimeException(s"FAILED PARSING WALLETS FILE: $errMsg")) + ) <* Log[F].info(s"Wallet loaded: ${line}") private def fromLine(line: String): Either[String, Vault] = line.split(",") match { case Array(ethAddressString, initRevBalanceStr, _) => @@ -70,12 +54,12 @@ object VaultParser { RevAddress .fromEthAddress(ethAddressString) .map(Vault(_, initRevBalance)) - .toRight(s"Ethereum address $ethAddressString is invalid.") + .toRight(s"INVALID ETH ADDRESS while parsing wallets file: $ethAddressString") case Failure(_) => - Left(s"Failed to parse given initial balance $initRevBalanceStr as positive long.") + Left(s"INVALID WALLET BALANCE $initRevBalanceStr. Please put positive long.") } - case _ => Left(s"Invalid vault specification:\n$line") + case _ => Left(s"INVALID WALLET FORMAT:\n$line") } } diff --git a/casper/src/test/scala/coop/rchain/casper/genesis/GenesisTest.scala b/casper/src/test/scala/coop/rchain/casper/genesis/GenesisTest.scala index 420f11561ac..1bb23401dd8 100644 --- a/casper/src/test/scala/coop/rchain/casper/genesis/GenesisTest.scala +++ b/casper/src/test/scala/coop/rchain/casper/genesis/GenesisTest.scala @@ -78,14 +78,14 @@ class GenesisTest extends FlatSpec with Matchers with EitherValues with BlockDag _ <- fromInputFiles()(runtimeManager, genesisPath, log, time) _ = log.warns.count( _.contains( - "Bonds file was not specified and default bonds file does not exist. Falling back on generating random validators." + "Creating file with random bonds" ) ) should be(1) - } yield log.infos.count(_.contains("Created validator")) should be(autogenShardSize) + } yield log.infos.count(_.contains("Bond generated")) should be(autogenShardSize) } ) - it should "fail with error when bonds file does not exist" in taskTest( + it should "tell when bonds file does not exist" in taskTest( withGenResources { ( runtimeManager: RuntimeManager[Task], @@ -100,9 +100,7 @@ class GenesisTest extends FlatSpec with Matchers with EitherValues with BlockDag log, time ).attempt - } yield genesisAttempt.left.value.getMessage should be( - "Specified bonds file not/a/real/file does not exist" - ) + } yield log.warns.exists(_.contains("BONDS FILE NOT FOUND")) } ) @@ -128,7 +126,7 @@ class GenesisTest extends FlatSpec with Matchers with EitherValues with BlockDag time ).attempt } yield genesisAttempt.left.value.getMessage should include( - "misformatted.txt cannot be parsed" + "FAILED PARSING BONDS FILE" ) } ) @@ -152,7 +150,7 @@ class GenesisTest extends FlatSpec with Matchers with EitherValues with BlockDag time ) bonds = ProtoUtil.bonds(genesis) - _ = log.infos.length should be(2) + _ = log.infos.length should be(3) result = validators .map { case (v, i) => Bond(ByteString.copyFrom(Base16.unsafeDecode(v)), i.toLong) @@ -238,12 +236,12 @@ object GenesisTest { ): Task[BlockMessage] = for { timestamp <- deployTimestamp.fold(Time[Task].currentMillis)(x => x.pure[Task]) - vaults <- VaultParser.parse[Task](maybeVaultsPath, genesisPath.resolve("wallets.txt")) + vaults <- VaultParser.parse[Task]( + maybeVaultsPath.getOrElse(genesisPath + "/wallets.txt") + ) bonds <- BondsParser.parse[Task]( - maybeBondsPath, - genesisPath.resolve("bonds.txt"), - autogenShardSize, - genesisPath + maybeBondsPath.getOrElse(genesisPath + "/bonds.txt"), + autogenShardSize ) validators = bonds.toSeq.map(Validator.tupled) genesisBlock <- createGenesisBlock( diff --git a/node/src/test/scala/coop/rchain/node/configuration/commandline/ConfigMapperSpec.scala b/node/src/test/scala/coop/rchain/node/configuration/commandline/ConfigMapperSpec.scala index 62b82b67448..cc03bda4dcb 100644 --- a/node/src/test/scala/coop/rchain/node/configuration/commandline/ConfigMapperSpec.scala +++ b/node/src/test/scala/coop/rchain/node/configuration/commandline/ConfigMapperSpec.scala @@ -214,8 +214,8 @@ class ConfigMapperSpec extends FunSuite with Matchers { ), genesisBlockData = GenesisBlockData( genesisDataDir = Paths.get("/var/lib/rnode/genesis"), - bondsFile = Some("/var/lib/rnode/genesis/bonds1.txt"), - walletsFile = Some("/var/lib/rnode/genesis/wallets1.txt"), + bondsFile = "/var/lib/rnode/genesis/bonds1.txt", + walletsFile = "/var/lib/rnode/genesis/wallets1.txt", bondMaximum = 111111, bondMinimum = 111111, epochLength = 111111, diff --git a/node/src/test/scala/coop/rchain/node/configuration/hocon/HoconConfigurationSpec.scala b/node/src/test/scala/coop/rchain/node/configuration/hocon/HoconConfigurationSpec.scala index ff982f6c87c..05f21d40d1b 100644 --- a/node/src/test/scala/coop/rchain/node/configuration/hocon/HoconConfigurationSpec.scala +++ b/node/src/test/scala/coop/rchain/node/configuration/hocon/HoconConfigurationSpec.scala @@ -124,8 +124,8 @@ class HoconConfigurationSpec extends FunSuite with Matchers { ), genesisBlockData = GenesisBlockData( genesisDataDir = Paths.get("/var/lib/rnode/genesis"), - bondsFile = Some("/var/lib/rnode/genesis/bonds.txt"), - walletsFile = Some("/var/lib/rnode/genesis/wallets.txt"), + bondsFile = "/var/lib/rnode/genesis/bonds.txt", + walletsFile = "/var/lib/rnode/genesis/wallets.txt", bondMaximum = 9223372036854775807L, bondMinimum = 1, epochLength = 10000,