Skip to content

Commit

Permalink
Merge pull request #1713 from ergoplatform/v4.0.31
Browse files Browse the repository at this point in the history
Candidate for version 4.0.31
  • Loading branch information
kushti authored May 30, 2022
2 parents 3fa0ed3 + ef04445 commit 1935c95
Show file tree
Hide file tree
Showing 26 changed files with 317 additions and 260 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ To run specific Ergo version `<VERSION>` as a service with custom config `/path/
-e MAX_HEAP=3G \
ergoplatform/ergo:<VERSION> --<networkId> -c /etc/myergo.conf

Available versions can be found on [Ergo Docker image page](https://hub.docker.com/r/ergoplatform/ergo/tags), for example, `v4.0.30`.
Available versions can be found on [Ergo Docker image page](https://hub.docker.com/r/ergoplatform/ergo/tags), for example, `v4.0.31`.

This will connect to the Ergo mainnet or testnet following your configuration passed in `myergo.conf` and network flag `--<networkId>`. Every default config value would be overwritten with corresponding value in `myergo.conf`. `MAX_HEAP` variable can be used to control how much memory can the node consume.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ trait ReemissionContracts {
val correctCoinsIssued = EQ(reemissionRewardPerBlock, Minus(ExtractAmount(Self), ExtractAmount(reemissionOut)))

// when reemission contract box got merged with other boxes
val sponsored = {
val merging = {
val feeOut = secondOut
AND(
GT(ExtractAmount(reemissionOut), ExtractAmount(Self)),
Expand All @@ -110,7 +110,7 @@ trait ReemissionContracts {
correctNftId,
sameScriptRule,
OR(
sponsored,
merging,
AND(
heightCorrect,
correctMinerOutput,
Expand Down
4 changes: 2 additions & 2 deletions papers/emission.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ second one is to pay mining fee supposedly (its value can be 0.01 ERG at most)
val correctCoinsIssued = EQ(reemissionRewardPerBlock, Minus(ExtractAmount(Self), ExtractAmount(reemissionOut)))

// when reemission contract box got merged with other boxes
val sponsored = AND(
val merging = AND(
GT(ExtractAmount(reemissionOut), ExtractAmount(Self)),
LE(ExtractAmount(ByIndex(Outputs, IntConstant(1))), LongConstant(10000000)), // 0.01 ERG
EQ(SizeOf(Outputs), 2)
Expand All @@ -136,7 +136,7 @@ second one is to pay mining fee supposedly (its value can be 0.01 ERG at most)
correctNftId,
sameScriptRule,
OR(
sponsored,
merging,
AND(
heightCorrect,
correctMinerOutput,
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/api/openapi.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
openapi: "3.0.2"

info:
version: "4.0.30"
version: "4.0.31"
title: Ergo Node API
description: API docs for Ergo Node. Models are shared between all Ergo products
contact:
Expand Down
9 changes: 8 additions & 1 deletion src/main/resources/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,13 @@ ergo {
# Number of keys to be generated for tests
# testKeysQty = 5

# Whitelisted tokens, if non-null, the wallet will automatically burn non-whitelisted tokens from
# inputs when doing transactions.
# If tokensWhitelist = [], all the tokens will be burnt,
# tokensWhitelist = ["example"] means that all the tokens except of "example" will be burnt
# tokensWhitelist = null means no tokens burnt automatically
tokensWhitelist = null

# Enable this setting to handle re-emission tokens in the wallet properly,
# e.g. doing transfers correctly in the presence of re-emission tokens
checkEIP27 = false
Expand Down Expand Up @@ -381,7 +388,7 @@ scorex {
nodeName = "ergo-node"

# Network protocol version to be sent in handshakes
appVersion = 4.0.30
appVersion = 4.0.31

# Network agent name. May contain information about client code
# stack, starting from core code-base up to the end graphical interface.
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/mainnet.conf
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ scorex {
network {
magicBytes = [1, 0, 2, 4]
bindAddress = "0.0.0.0:9030"
nodeName = "ergo-mainnet-4.0.30"
nodeName = "ergo-mainnet-4.0.31"
nodeName = ${?NODENAME}
knownPeers = [
"213.239.193.208:9030",
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/testnet.conf
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ scorex {
network {
magicBytes = [2, 0, 0, 2]
bindAddress = "0.0.0.0:9020"
nodeName = "ergo-testnet-4.0.30"
nodeName = "ergo-testnet-4.0.31"
nodeName = ${?NODENAME}
knownPeers = [
"213.239.193.208:9020",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class ErgoStatsCollector(readersHolder: ActorRef,
lastIncomingMessageTime = networkTime(),
None,
LaunchParameters,
eip27Supported = false)
eip27Supported = true)

override def receive: Receive =
onConnectedPeers orElse
Expand All @@ -91,8 +91,7 @@ class ErgoStatsCollector(readersHolder: ActorRef,
genesisBlockIdOpt = h.headerIdsAtHeight(ErgoHistory.GenesisHeight).headOption,
stateRoot = Some(Algos.encode(s.rootHash)),
stateVersion = Some(s.version),
parameters = s.stateContext.currentParameters,
eip27Supported = s.stateContext.eip27Supported
parameters = s.stateContext.currentParameters
)
}

Expand All @@ -108,7 +107,7 @@ class ErgoStatsCollector(readersHolder: ActorRef,
private def onStateChanged: Receive = {
case ChangedState(s: ErgoStateReader@unchecked) =>
val sc = s.stateContext
nodeInfo = nodeInfo.copy(parameters = sc.currentParameters, eip27Supported = sc.eip27Supported)
nodeInfo = nodeInfo.copy(parameters = sc.currentParameters)
}

private def onHistoryChanged: Receive = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -627,12 +627,8 @@ object CandidateGenerator extends ScorexLogging {
// forming transaction collecting emission
val reemissionSettings = chainSettings.reemission
val reemissionRules = reemissionSettings.reemissionRules
val eip27Supported = stateContext.eip27Supported
val eip27ActivationHeight = if (eip27Supported) {
reemissionSettings.activationHeight
} else {
Int.MaxValue
}

val eip27ActivationHeight = reemissionSettings.activationHeight
val reemissionTokenId = Digest32 @@ reemissionSettings.reemissionTokenIdBytes

val nextHeight = currentHeight + 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,105 +217,96 @@ case class ErgoTransaction(override val inputs: IndexedSeq[Input],
// we check that we're in utxo mode, as eip27Supported flag available only in this mode
// if we're in digest mode, skip validation
// todo: this check could be removed after EIP-27 activation
if (stateContext.ergoSettings.nodeSettings.stateType.holdsUtxoSet) {
lazy val reemissionSettings = stateContext.ergoSettings.chainSettings.reemission
lazy val reemissionRules = reemissionSettings.reemissionRules
lazy val reemissionSettings = stateContext.ergoSettings.chainSettings.reemission
lazy val reemissionRules = reemissionSettings.reemissionRules

lazy val reemissionTokenId = ModifierId @@ reemissionSettings.reemissionTokenId
lazy val reemissionTokenIdBytes = reemissionSettings.reemissionTokenIdBytes
lazy val reemissionTokenId = ModifierId @@ reemissionSettings.reemissionTokenId
lazy val reemissionTokenIdBytes = reemissionSettings.reemissionTokenIdBytes

lazy val emissionNftId = ModifierId @@ reemissionSettings.emissionNftId
lazy val emissionNftIdBytes = reemissionSettings.emissionNftIdBytes
lazy val emissionNftId = ModifierId @@ reemissionSettings.emissionNftId
lazy val emissionNftIdBytes = reemissionSettings.emissionNftIdBytes

lazy val chainSettings = stateContext.ergoSettings.chainSettings
lazy val emissionRules = chainSettings.emissionRules
lazy val chainSettings = stateContext.ergoSettings.chainSettings
lazy val emissionRules = chainSettings.emissionRules

lazy val height = stateContext.currentHeight
lazy val eip27Supported = stateContext.eip27Supported
lazy val height = stateContext.currentHeight

// considering voting for eip27 done, via eip27Supported flag
val activationHeight = if (eip27Supported) {
reemissionSettings.activationHeight
} else {
Int.MaxValue
}
val activationHeight = reemissionSettings.activationHeight

if (stateContext.currentHeight >= activationHeight) {
// reemission check logic below
var reemissionSpending = false
boxesToSpend.foreach { box =>
// checking EIP-27 rules for emission box
// for efficiency, skip boxes with less than 100K ERG
if (box.value > 100000 * EmissionRules.CoinsInOneErgo) {
// on activation height, emissionNft is not in emission box yet, but in injection box
// injection box index (1) is enforced by injection box contract
if (box.tokens.contains(emissionNftId) ||
(height == activationHeight && boxesToSpend(1).tokens.contains(emissionNftId))) {

if (stateContext.currentHeight >= activationHeight) {
// reemission check logic below
var reemissionSpending = false
boxesToSpend.foreach { box =>
// checking EIP-27 rules for emission box
// for efficiency, skip boxes with less than 100K ERG
if (box.value > 100000 * EmissionRules.CoinsInOneErgo) {
// on activation height, emissionNft is not in emission box yet, but in injection box
if (box.tokens.contains(emissionNftId) ||
(height == activationHeight && boxesToSpend(1).tokens.contains(emissionNftId))) {

// if emission contract NFT is in the input, remission tokens should be there also
val reemissionTokensIn = if (height == activationHeight) {
boxesToSpend(1).tokens.getOrElse(reemissionTokenId, 0L)
} else {
box.tokens.getOrElse(reemissionTokenId, 0L)
}
require(reemissionTokensIn > 0, "No re-emission tokens in the emission or injection box")

// output positions guaranteed by emission contract
val emissionOut = outputCandidates(0)
val rewardsOut = outputCandidates(1)

// check positions of emission NFT and reemission token
val firstEmissionBoxTokenId = emissionOut.additionalTokens.apply(0)._1
val secondEmissionBoxTokenId = emissionOut.additionalTokens.apply(1)._1
require(
firstEmissionBoxTokenId.sameElements(emissionNftIdBytes),
"No emission box NFT in the emission box"
)
require(
secondEmissionBoxTokenId.sameElements(reemissionTokenIdBytes),
"No re-emission token in the emission box"
)

//we're checking how emission box is paying reemission tokens below
val emissionTokensOut = emissionOut.tokens.getOrElse(reemissionTokenId, 0L)
val rewardsTokensOut = rewardsOut.tokens.getOrElse(reemissionTokenId, 0L)
require(reemissionTokensIn == emissionTokensOut + rewardsTokensOut, "Reemission tokens not preserved")

val properReemissionRewardPart = reemissionRules.reemissionForHeight(height, emissionRules)
require(rewardsTokensOut == properReemissionRewardPart, "Rewards out condition violated")
// if emission contract NFT is in the input, remission tokens should be there also
val reemissionTokensIn = if (height == activationHeight) {
boxesToSpend(1).tokens.getOrElse(reemissionTokenId, 0L)
} else {
//this path can be removed after EIP-27 activation
if (height >= activationHeight && box.ergoTree == chainSettings.monetary.emissionBoxProposition) {
//we require emission contract NFT and reemission token to be presented in emission output
val emissionOutTokens = outputCandidates(0).tokens
require(emissionOutTokens.contains(emissionNftId))
require(emissionOutTokens.contains(reemissionTokenId))
}
box.tokens.getOrElse(reemissionTokenId, 0L)
}
require(reemissionTokensIn > 0, "No re-emission tokens in the emission or injection box")

// output positions guaranteed by emission contract
val emissionOut = outputCandidates(0)
val rewardsOut = outputCandidates(1)

// check positions of emission NFT and reemission token
val firstEmissionBoxTokenId = emissionOut.additionalTokens.apply(0)._1
val secondEmissionBoxTokenId = emissionOut.additionalTokens.apply(1)._1
require(
firstEmissionBoxTokenId.sameElements(emissionNftIdBytes),
"No emission box NFT in the emission box"
)
require(
secondEmissionBoxTokenId.sameElements(reemissionTokenIdBytes),
"No re-emission token in the emission box"
)

//we're checking how emission box is paying reemission tokens below
val emissionTokensOut = emissionOut.tokens.getOrElse(reemissionTokenId, 0L)
val rewardsTokensOut = rewardsOut.tokens.getOrElse(reemissionTokenId, 0L)
require(reemissionTokensIn == emissionTokensOut + rewardsTokensOut, "Reemission tokens not preserved")

val properReemissionRewardPart = reemissionRules.reemissionForHeight(height, emissionRules)
require(rewardsTokensOut == properReemissionRewardPart, "Rewards out condition violated")
} else {
//this path can be removed after EIP-27 activation
if (height >= activationHeight && box.ergoTree == chainSettings.monetary.emissionBoxProposition) {
//we require emission contract NFT and reemission token to be presented in emission output
val emissionOutTokens = outputCandidates(0).tokens
require(emissionOutTokens.contains(emissionNftId))
require(emissionOutTokens.contains(reemissionTokenId))
}
} else if (box.tokens.contains(reemissionTokenId) && height > activationHeight) {
// reemission tokens spent after EIP-27 activation
reemissionSpending = true
}
} else if (box.tokens.contains(reemissionTokenId) && height > activationHeight) {
// reemission tokens spent after EIP-27 activation
reemissionSpending = true
}
}

// if box with reemission tokens spent
if (reemissionSpending) {
val payToReemissionContract = reemissionRules.payToReemission
val toBurn = boxesToSpend.map { box =>
box.tokens.getOrElse(reemissionTokenId, 0L)
}.sum
log.debug(s"Reemission tokens to burn: $toBurn")
val reemissionOutputs = outputCandidates.filter { out =>
require(!out.tokens.contains(reemissionTokenId), "outputs contain reemission token")
out.ergoTree == payToReemissionContract
}
val sentToReemission = reemissionOutputs.map(_.value).sum
require(sentToReemission == toBurn, "Burning condition violated")
// if box with reemission tokens spent
if (reemissionSpending) {
val payToReemissionContract = reemissionRules.payToReemission
val toBurn = boxesToSpend.map { box =>
box.tokens.getOrElse(reemissionTokenId, 0L)
}.sum
log.debug(s"Reemission tokens to burn: $toBurn")
val reemissionOutputs = outputCandidates.filter { out =>
require(!out.tokens.contains(reemissionTokenId), "outputs contain reemission token")
out.ergoTree == payToReemissionContract
}
} else {
Success(())
val sentToReemission = reemissionOutputs.map(_.value).sum
require(sentToReemission == toBurn, "Burning condition violated")
}
} else {
log.warn("Checking EIP-27 in digest mode")
Success(())
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,7 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti
val constants = StateConstants(settings)
restoreConsistentState(ErgoState.readOrGenerate(settings, constants).asInstanceOf[State], history) match {
case Success(state) =>
log.info(s"State database read, state synchronized, " +
s"eip27 supported == ${state.stateContext.eip27Supported}")
log.info(s"State database read, state synchronized")
val wallet = ErgoWallet.readOrGenerate(
history.getReader.asInstanceOf[ErgoHistoryReader],
settings,
Expand Down
Loading

0 comments on commit 1935c95

Please sign in to comment.