Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ object MainnetLaunchParameters extends Parameters(height = 0,
* Parameters corresponding to the genesis block in the public testnet
*/
object TestnetLaunchParameters extends Parameters(height = 0,
parametersTable = Parameters.DefaultParameters,
proposedUpdate = ErgoValidationSettingsUpdate.empty)
parametersTable = Parameters.DefaultParameters.updated(Parameters.BlockVersion, Header.Interpreter60Version),
proposedUpdate = ErgoValidationSettingsUpdate(Seq(215, 409), Seq.empty))

/**
* Initial parameters corresponding to a devnet which is starting with 5.0 activated
Expand Down
9 changes: 4 additions & 5 deletions src/it/scala/org/ergoplatform/it/container/Docker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import com.typesafe.config.{Config, ConfigFactory, ConfigRenderOptions}
import net.ceedubs.ficus.Ficus._
import org.apache.commons.io.FileUtils
import org.asynchttpclient.Dsl.{config, _}
import org.ergoplatform.settings.NetworkType.{DevNet, DevNet60, MainNet, TestNet}
import org.ergoplatform.settings.NetworkType.{DevNet, DevNet60, MainNet, TestNet, Tests}
import org.ergoplatform.settings.{ErgoSettings, ErgoSettingsReader, NetworkType}
import scorex.util.ScorexLogging

Expand Down Expand Up @@ -145,7 +145,7 @@ class Docker(
val networkPort = initialSettings.scorexSettings.network.bindAddress.getPort

val nodeConfig: Config =
enrichNodeConfig(networkType, nodeSpecificConfig, extraConfig, ip, networkPort)
enrichNodeConfig(networkType, nodeSpecificConfig, extraConfig)
val settings: ErgoSettings = buildErgoSettings(networkType, nodeConfig)
val containerBuilder: CreateContainerCmd =
buildPeerContainerCmd(networkType, nodeConfig, settings, ip, specialVolumeOpt)
Expand Down Expand Up @@ -219,9 +219,7 @@ class Docker(
private def enrichNodeConfig(
networkType: NetworkType,
nodeConfig: Config,
extraConfig: ExtraConfig,
ip: String,
port: Int
extraConfig: ExtraConfig
) = {
val publicPeerConfig = nodeConfig //.withFallback(declaredAddressConfig(ip, port))
val withPeerConfig = nodeRepository.headOption.fold(publicPeerConfig) { node =>
Expand Down Expand Up @@ -296,6 +294,7 @@ class Docker(
val networkTypeCmdOption = networkType match {
case MainNet => "--mainnet"
case TestNet => "--testnet"
case Tests => "--testnet"
case DevNet => ""
case DevNet60 => ""
}
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ ergo {
activationEpochs = 32

# Activation height for protocol version 2 (client version 4.0.0 hard-fork)
# Relevant for the mainnet only atm
version2ActivationHeight = 417792

# Difficulty for Autolykos version 2 activation (corresponding to ~ 1 TH/s hashrate)
Expand Down
36 changes: 13 additions & 23 deletions src/main/resources/testnet.conf
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,7 @@ ergo {

# Dump ADProofs only for the suffix given during bootstrapping
adProofsSuffixLength = 114688 // 112k

# As some v.3 blocks in the PaiNet are violating monotonic creation height rule (due to 5.0 being activated before
# the monotonic introduced), this checkpoint is mandatory
checkpoint = {
height = 91320
blockId = "fd06abdf0e6558ebaaf524b654c922a1cb42e542ae49d1c4a79397a077209278"
}
}
}

chain {
protocolVersion = 4 # 6.0 soft-fork
Expand Down Expand Up @@ -57,51 +50,48 @@ ergo {

# Voting epochs to activate a soft-fork after acceptance
activationEpochs = 32

# Activation height for testnet protocol version 2 (client version 4.0.0 hard-fork)
version2ActivationHeight = 128

version2ActivationDifficultyHex = "20"
}

reemission {
checkReemissionRules = false

emissionNftId = "06f29034fb69b23d519f84c4811a19694b8cdc2ce076147aaa050276f0b840f4"
# emissionNftId = "06f29034fb69b23d519f84c4811a19694b8cdc2ce076147aaa050276f0b840f4"

reemissionTokenId = "01345f0ed87b74008d1c46aefd3e7ad6ee5909a2324f2899031cdfee3cc1e022"
# reemissionTokenId = "01345f0ed87b74008d1c46aefd3e7ad6ee5909a2324f2899031cdfee3cc1e022"

reemissionNftId = "06f2c3adfe52304543f7b623cc3fccddc0174a7db52452fef8e589adacdfdfee"
# reemissionNftId = "06f2c3adfe52304543f7b623cc3fccddc0174a7db52452fef8e589adacdfdfee"

# no re-emission
activationHeight = 100000001

reemissionStartHeight = 1860400

injectionBoxBytesEncoded = "a0f9e1b5fb011003040005808098f4e9b5ca6a0402d1ed91c1b2a4730000730193c5a7c5b2a4730200f6ac0b0201345f0ed87b74008d1c46aefd3e7ad6ee5909a2324f2899031cdfee3cc1e02280808cfaf49aa53506f29034fb69b23d519f84c4811a19694b8cdc2ce076147aaa050276f0b840f40100325c3679e7e0e2f683e4a382aa74c2c1cb989bb6ad6a1d4b1c5a021d7b410d0f00"
# injectionBoxBytesEncoded = "a0f9e1b5fb011003040005808098f4e9b5ca6a0402d1ed91c1b2a4730000730193c5a7c5b2a4730200f6ac0b0201345f0ed87b74008d1c46aefd3e7ad6ee5909a2324f2899031cdfee3cc1e02280808cfaf49aa53506f29034fb69b23d519f84c4811a19694b8cdc2ce076147aaa050276f0b840f40100325c3679e7e0e2f683e4a382aa74c2c1cb989bb6ad6a1d4b1c5a021d7b410d0f00"
}

# Base16 representation of genesis state roothash
genesisStateDigestHex = "cb63aa99a3060f341781d8662b58bf18b9ad258db4fe88d09f8f71cb668cad4502"
}

voting {
120 = 1 // vote for 5.0 soft-fork, the vote will not be given before block 4,096
# 120 = 1 // vote for a soft-fork
}

wallet.secretStorage.secretDir = ${ergo.directory}"/wallet/keystore"
}

scorex {
network {
magicBytes = [2, 0, 2, 3]
bindAddress = "0.0.0.0:9022"
magicBytes = [2, 3, 2, 3]
bindAddress = "0.0.0.0:9023"
nodeName = "ergo-testnet-"${scorex.network.appVersion}
nodeName = ${?NODENAME}
knownPeers = [
"213.239.193.208:9022",
"168.138.185.215:9022",
"192.234.196.165:9022"
"213.239.193.208:9023",
"168.138.185.215:9023",
"192.234.196.165:9023"
]
penaltyScoreThreshold = 500000
}
restApi {
# Hex-encoded Blake2b256 hash of an API key. Should be 64-chars long Base16 string.
Expand Down
28 changes: 26 additions & 2 deletions src/main/scala/org/ergoplatform/http/api/MiningApiRoute.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import akka.actor.{ActorRef, ActorRefFactory}
import akka.http.scaladsl.server.Route
import akka.pattern.ask
import io.circe.syntax._
import io.circe.{Encoder, Json}
import io.circe.{Decoder, Encoder, Json}
import org.bouncycastle.util.encoders.Hex
import org.ergoplatform.http.api.requests.MiningRequest
import org.ergoplatform.mining.CandidateGenerator.Candidate
import org.ergoplatform.mining.{AutolykosSolution, CandidateGenerator, ErgoMiner}
import org.ergoplatform.modifiers.mempool.ErgoTransaction
Expand All @@ -13,8 +15,10 @@ import org.ergoplatform.settings.{ErgoSettings, RESTApiSettings}
import org.ergoplatform.{ErgoAddress, ErgoTreePredef, Pay2SAddress}
import scorex.core.api.http.ApiResponse
import sigma.data.ProveDlog
import sigma.serialization.GroupElementSerializer

import scala.concurrent.Future
import scala.util.{Failure, Success, Try}

case class MiningApiRoute(miner: ActorRef,
ergoSettings: ErgoSettings)
Expand All @@ -23,10 +27,16 @@ case class MiningApiRoute(miner: ActorRef,
val settings: RESTApiSettings = ergoSettings.scorexSettings.restApi

implicit val addressEncoder: Encoder[ErgoAddress] = ErgoAddressJsonEncoder(ergoSettings.chainSettings).encoder

implicit val miningRequestDecoder: Decoder[MiningRequest] = { cursor =>
for {
txs <- cursor.downField("txs").as[Seq[ErgoTransaction]]
pk <- cursor.downField("pk").as[String]
} yield MiningRequest(txs, pk)
}
override val route: Route = pathPrefix("mining") {
candidateR ~
candidateWithTxsR ~
candidateWithTxsAndPkR ~
solutionR ~
rewardAddressR ~
rewardPublicKeyR
Expand All @@ -53,6 +63,20 @@ case class MiningApiRoute(miner: ActorRef,
ApiResponse(candidateF)
}

def candidateWithTxsAndPkR: Route = (path("candidateWithTxsAndPk")
& post & entity(as[MiningRequest]) & withAuth) { txsAndPk =>
val tryPk = Try(GroupElementSerializer.fromBytes(Hex.decode(txsAndPk.pk)))
val result = tryPk match {
case Failure(_) =>
Future.failed(new Exception("Could not decode hexadecimal string for given public key"))
case Success(pk) =>
val prepareCmd = CandidateGenerator.GenerateCandidate(txsAndPk.txs, reply = true,
forced = false, Some(ProveDlog.apply(pk)))
miner.askWithStatus(prepareCmd).mapTo[Candidate].map(_.externalVersion)
}
ApiResponse(result)
}

def solutionR: Route = (path("solution") & post & entity(as[AutolykosSolution])) { solution =>
val result = if (ergoSettings.nodeSettings.useExternalMiner) {
miner.askWithStatus(solution).mapTo[Unit]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.ergoplatform.http.api.requests

import org.ergoplatform.modifiers.mempool.ErgoTransaction

/**
* Represents a request to generate a candidate with the given transactions and miner public key.
*
* @param txs Transactions to include in the block candidate
* @param pk String Hexadecimal representation of public key to use as minerPk
*/
case class MiningRequest(txs: Seq[ErgoTransaction], pk: String)
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ class CandidateGenerator(
context.become(initialized(state))
}

case gen @ GenerateCandidate(txsToInclude, reply, forced) =>
case gen @ GenerateCandidate(txsToInclude, reply, forced, optPk) =>
val senderOpt = if (reply) Some(sender()) else None
if (!forced && cachedFor(state.cachedCandidate, txsToInclude)) {
senderOpt.foreach(_ ! StatusReply.success(state.cachedCandidate.get))
Expand All @@ -162,7 +162,7 @@ class CandidateGenerator(
state.hr,
state.sr,
state.mpr,
minerPk,
optPk.getOrElse(minerPk),
txsToInclude,
ergoSettings
) match {
Expand Down Expand Up @@ -260,7 +260,8 @@ object CandidateGenerator extends ScorexLogging {
case class GenerateCandidate(
txsToInclude: Seq[ErgoTransaction],
reply: Boolean,
forced: Boolean
forced: Boolean,
optPk: Option[ProveDlog] = None
)

/** Local state of candidate generator to avoid mutable vars */
Expand Down
4 changes: 2 additions & 2 deletions src/main/scala/org/ergoplatform/mining/ErgoMiner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -156,15 +156,15 @@ class ErgoMiner(
log.info("Starting mining triggered by incoming block")
self ! StartMining

case GenerateCandidate(_, _, _) =>
case GenerateCandidate(_, _, _, _) =>
sender() ! StatusReply.error("Miner has not started yet")
}

/** Bridge between external miner and CandidateGenerator (Internal mining threads are talking to CandidateGenerator directly.)
* The reason is that replying is optional and it is not possible to obtain a sender reference from MiningApiRoute 'ask'.
*/
def started(minerState: MinerState): Receive = {
case genCandidate @ GenerateCandidate(_, _, _) =>
case genCandidate @ GenerateCandidate(_, _, _, _) =>
minerState.candidateGeneratorRef forward genCandidate

case solution: AutolykosSolution =>
Expand Down
4 changes: 3 additions & 1 deletion src/main/scala/org/ergoplatform/settings/ErgoSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ case class ErgoSettings(directory: String,
Devnet60LaunchParameters
} else if (networkType == NetworkType.TestNet) {
TestnetLaunchParameters
} else {
} else if (networkType == NetworkType.Tests) {
MainnetLaunchParameters
}else {
MainnetLaunchParameters
}
}
Expand Down
8 changes: 8 additions & 0 deletions src/main/scala/org/ergoplatform/settings/NetworkType.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ object NetworkType {
override val addressPrefix: Byte = ErgoAddressEncoder.TestnetNetworkPrefix
}

// Synthetic network type
case object Tests extends NetworkType {
override val verboseName: String = "tests"
override val isMainNet: Boolean = false
override val isTestNet: Boolean = true
override val addressPrefix: Byte = ErgoAddressEncoder.TestnetNetworkPrefix
}


// devnet which is starting from 5.0 activated since genesis block
case object DevNet extends NetworkType {
Expand Down
60 changes: 0 additions & 60 deletions src/test/scala/org/ergoplatform/mining/ErgoMinerSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -369,64 +369,4 @@ class ErgoMinerSpec extends AnyFlatSpec with ErgoTestHelpers with Eventually {
system.terminate()
}

it should "mine after HF" in new TestKit(ActorSystem()) {
val forkHeight = 3

val testProbe = new TestProbe(system)
system.eventStream.subscribe(testProbe.ref, newBlockSignal)

val forkSettings: ErgoSettings = {
val empty = ErgoSettingsReader.read()

val nodeSettings = empty.nodeSettings.copy(mining = true,
stateType = StateType.Utxo,
internalMinerPollingInterval = 2.second,
offlineGeneration = true,
verifyTransactions = true)
val chainSettings = empty.chainSettings.copy(
blockInterval = 2.seconds,
epochLength = forkHeight,
voting = empty.chainSettings.voting.copy(
version2ActivationHeight = forkHeight,
version2ActivationDifficultyHex = "10",
votingLength = forkHeight)
)
empty.copy(nodeSettings = nodeSettings, chainSettings = chainSettings, directory = createTempDir.getAbsolutePath)
}

val nodeViewHolderRef: ActorRef = ErgoNodeViewRef(forkSettings)
val readersHolderRef: ActorRef = ErgoReadersHolderRef(nodeViewHolderRef)

val minerRef: ActorRef = ErgoMiner(
forkSettings,
nodeViewHolderRef,
readersHolderRef,
Some(defaultMinerSecret)
)

minerRef ! StartMining

testProbe.expectMsgClass(newBlockDelay, newBlockSignal)
testProbe.expectMsgClass(newBlockDelay, newBlockSignal)
testProbe.expectMsgClass(newBlockDelay, newBlockSignal)
testProbe.expectMsgClass(newBlockDelay, newBlockSignal)

val wm1 = getWorkMessage(minerRef, Seq.empty)
(wm1.h.get >= forkHeight) shouldBe true

testProbe.expectMsgClass(newBlockDelay, newBlockSignal)
implicit val patienceConfig: PatienceConfig = PatienceConfig(1.seconds, 50.millis)
eventually {
val wm2 = getWorkMessage(minerRef, Seq.empty)
(wm2.h.get >= forkHeight) shouldBe true
wm1.msg.sameElements(wm2.msg) shouldBe false

val v2Block = testProbe.expectMsgClass(newBlockDelay, newBlockSignal)

val h2 = v2Block.header
h2.version shouldBe 2
h2.minerPk shouldBe defaultMinerPk.value
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.ergoplatform.settings.Parameters.{MaxBlockCostIncrease, MinValuePerBy
import org.ergoplatform.settings._
import org.ergoplatform.wallet.interpreter.ErgoInterpreter
import org.ergoplatform.ErgoBox
import org.ergoplatform.settings.NetworkType.Tests
import scorex.util.ScorexLogging

import scala.concurrent.duration._
Expand All @@ -23,7 +24,9 @@ object ErgoNodeTestConstants extends ScorexLogging {
Parameters(0, Parameters.DefaultParameters ++ extension, ErgoValidationSettingsUpdate.empty)
}

val initSettings: ErgoSettings = ErgoSettingsReader.read(Args(Some("src/test/resources/application.conf"), None))
val initSettings: ErgoSettings = ErgoSettingsReader
.read(Args(Some("src/test/resources/application.conf"), None))
.copy(networkType = Tests)

implicit val settings: ErgoSettings = initSettings

Expand Down
2 changes: 1 addition & 1 deletion src/test/scala/org/ergoplatform/utils/Stubs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ trait Stubs extends ErgoTestHelpers with TestFileUtils {

class MinerStub extends Actor {
def receive: Receive = {
case CandidateGenerator.GenerateCandidate(_, reply, _) =>
case CandidateGenerator.GenerateCandidate(_, reply, _, _) =>
if (reply) {
val candidate = Candidate(null, externalWorkMessage, Seq.empty) // API does not use CandidateBlock
sender() ! StatusReply.success(candidate)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,10 @@ object ErgoNodeTransactionGenerators extends ScorexLogging {
} while (assetsMap.nonEmpty && availableTokenSlots > 0)
}

val creationHeight = boxesToSpend.map(_.creationHeight).max
val newBoxes = outputAmounts.zip(tokenAmounts.toIndexedSeq).map { case (amt, tokens) =>
val normalizedTokens = tokens.toSeq.map(t => t._1.data.toTokenId -> t._2)
testBox(amt, outputsProposition, 0, normalizedTokens)
testBox(amt, outputsProposition, creationHeight, normalizedTokens)
}
val inputs = boxesToSpend.map(b => Input(b.id, emptyProverResult))
val dataInputs = dataBoxes.map(b => DataInput(b.id))
Expand Down
Loading