diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index d222647106..b2ed2b3d16 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -11,6 +11,11 @@ jobs: steps: - name: Check out the repo uses: actions/checkout@v2 + + - name: qemu + uses: docker/setup-qemu-action@v1 + + - uses: docker/setup-buildx-action@v1 - name: Docker Login uses: docker/login-action@v1.10.0 @@ -29,5 +34,6 @@ jobs: with: context: . push: true + platforms: linux/amd64,linux/arm64 tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/Dockerfile b/Dockerfile index 1c724297cf..1c5f95e3c4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mozilla/sbt:11.0.8_1.3.13 as builder +FROM sbtscala/scala-sbt:eclipse-temurin-11.0.15_1.6.2_2.12.16 as builder WORKDIR /mnt COPY build.sbt findbugs-exclude.xml ./ COPY project/ project/ @@ -12,15 +12,15 @@ COPY . ./ RUN sbt assembly RUN mv `find target/scala-*/stripped/ -name ergo-*.jar` ergo.jar -FROM openjdk:11-jre-slim +FROM eclipse-temurin:11-jre-jammy RUN apt-get update && apt-get install -y curl jq && rm -rf /var/lib/apt/lists/* RUN adduser --disabled-password --home /home/ergo --uid 9052 --gecos "ErgoPlatform" ergo && \ install -m 0750 -o ergo -g ergo -d /home/ergo/.ergo USER ergo -EXPOSE 9020 9052 9030 9053 +EXPOSE 9020 9021 9022 9052 9030 9053 WORKDIR /home/ergo VOLUME ["/home/ergo/.ergo"] ENV MAX_HEAP 3G -ENV _JAVA_OPTIONS "-Xmx${MAX_HEAP}" +ENV _JAVA_OPTIONS "-Xms${MAX_HEAP} -Xmx${MAX_HEAP}" COPY --from=builder /mnt/ergo.jar /home/ergo/ergo.jar ENTRYPOINT ["java", "-jar", "/home/ergo/ergo.jar"] diff --git a/README.md b/README.md index 3e748ba742..199be06110 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ To run specific Ergo version `` as a service with custom config `/path/ -e MAX_HEAP=3G \ ergoplatform/ergo: -- -c /etc/myergo.conf -Available versions can be found on [Ergo Docker image page](https://hub.docker.com/r/ergoplatform/ergo/tags), for example, `v5.0.1`. +Available versions can be found on [Ergo Docker image page](https://hub.docker.com/r/ergoplatform/ergo/tags), for example, `v5.0.2`. This will connect to the Ergo mainnet or testnet following your configuration passed in `myergo.conf` and network flag `--`. 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. diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/BoxSelector.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/BoxSelector.scala index 00fff87838..c678893907 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/BoxSelector.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/BoxSelector.scala @@ -48,9 +48,7 @@ trait BoxSelector extends ScorexLogging { */ def reemissionAmount[T <: ErgoBoxAssets](boxes: Seq[T]): Long = { reemissionDataOpt.map { reemissionData => - boxes - .flatMap(_.tokens.get(reemissionData.reemissionTokenId)) - .sum + boxes.foldLeft(0L) { case (sum, b) => sum + b.tokens.getOrElse(reemissionData.reemissionTokenId, 0L) } }.getOrElse(0L) } diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelector.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelector.scala index a70c1c8bdb..a4aad20681 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelector.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelector.scala @@ -136,7 +136,7 @@ class DefaultBoxSelector(override val reemissionDataOpt: Option[ReemissionData]) targetBoxAssets: TokensMap, reemissionAmt: Long): Either[BoxSelectionError, Seq[ErgoBoxAssets]] = { AssetUtils.subtractAssetsMut(foundBoxAssets, targetBoxAssets) - val changeBoxesAssets: Seq[mutable.Map[ModifierId, Long]] = foundBoxAssets.grouped(MaxAssetsPerBox).toSeq + val changeBoxesAssets: Seq[mutable.Map[ModifierId, Long]] = foundBoxAssets.grouped(MaxAssetsPerBox).toIndexedSeq val changeBalance = foundBalance - targetBalance //at least a minimum amount of ERG should be assigned per a created box if (changeBoxesAssets.size * MinBoxValue > changeBalance) { diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ReplaceCompactCollectBoxSelector.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ReplaceCompactCollectBoxSelector.scala index afe406cbf4..16a010ba5e 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ReplaceCompactCollectBoxSelector.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ReplaceCompactCollectBoxSelector.scala @@ -9,6 +9,7 @@ import org.ergoplatform.wallet.transactions.TransactionBuilder._ import scala.annotation.tailrec import scala.collection.mutable +import scala.collection.mutable.ListBuffer /** * A box selector which is parameterized by maximum number of inputs a transaction can have, and optimal number of inputs. @@ -84,7 +85,7 @@ class ReplaceCompactCollectBoxSelector(maxInputs: Int, targetBalance: Long, targetAssets: TokensMap ): Either[BoxSelectionError, Seq[ErgoBoxAssets]] = { - val compactedBalance = boxes.map(b => BoxSelector.valueOf(b, reemissionDataOpt)).sum + val compactedBalance = boxes.foldLeft(0L) { case (sum, b) => sum + BoxSelector.valueOf(b, reemissionDataOpt) } val compactedAssets = mutable.Map[ModifierId, Long]() AssetUtils.mergeAssetsMut(compactedAssets, boxes.map(_.tokens): _*) val ra = reemissionAmount(boxes) @@ -108,14 +109,16 @@ class ReplaceCompactCollectBoxSelector(maxInputs: Int, targetBalance: Long, targetAssets: TokensMap): Either[BoxSelectionError, BoxSelectionResult[T]] = { val boxes = bsr.boxes - val diff = boxes.map(b => BoxSelector.valueOf(b,reemissionDataOpt)).sum - targetBalance + val diff = boxes.foldLeft(0L) { case (sum, b) => sum + BoxSelector.valueOf(b, reemissionDataOpt) } - targetBalance - val boxesToThrowAway = boxes.filter(!_.tokens.keySet.exists(tid => targetAssets.keySet.contains(tid))) - val sorted = boxesToThrowAway.sortBy(b => BoxSelector.valueOf(b, reemissionDataOpt)) + val targetAssetsKeys = targetAssets.keySet + val sortedBoxesToThrowAway = + boxes.filter(!_.tokens.keySet.exists(tid => targetAssetsKeys.contains(tid))) + .sortBy(b => BoxSelector.valueOf(b, reemissionDataOpt)) - if (diff >= BoxSelector.valueOf(sorted.head, reemissionDataOpt)) { + if (diff >= BoxSelector.valueOf(sortedBoxesToThrowAway.head, reemissionDataOpt)) { var thrownValue = 0L - val thrownBoxes = sorted.takeWhile { b => + val thrownBoxes = sortedBoxesToThrowAway.takeWhile { b => thrownValue = thrownValue + BoxSelector.valueOf(b, reemissionDataOpt) thrownValue <= diff } @@ -135,31 +138,33 @@ class ReplaceCompactCollectBoxSelector(maxInputs: Int, val boxesToThrowAway = bsr.boxes.filter(!_.tokens.keySet.exists(tid => targetAssets.keySet.contains(tid))) val sorted = boxesToThrowAway.sortBy(b => BoxSelector.valueOf(b, reemissionDataOpt)) - type BoxesToAdd = Seq[T] - type BoxesToDrop = Seq[T] - type Operations = (BoxesToAdd, BoxesToDrop) + val boxesToAdd = ListBuffer.empty[T] + val boxesToDrop = mutable.HashSet.empty[T] @tailrec - def replaceStep(candidates: Seq[T], toDrop: Seq[T], currentOps: Operations): Operations = { + def replaceStep(candidates: Seq[T], toDrop: Seq[T]): Unit = { candidates match { - case Seq() => currentOps + case Seq() => () case Seq(cand) if BoxSelector.valueOf(cand, reemissionDataOpt) <= toDrop.headOption.map(b => BoxSelector.valueOf(b, reemissionDataOpt)).getOrElse(Long.MaxValue) => - currentOps + () case Seq(cand, cs@_*) => var collected = 0L - val (dropped, remain) = toDrop.partition { b => + val candValue = BoxSelector.valueOf(cand, reemissionDataOpt) + val (dropped, remain) = toDrop.span { b => collected = collected + BoxSelector.valueOf(b, reemissionDataOpt) - collected <= BoxSelector.valueOf(cand, reemissionDataOpt) + collected <= candValue } - replaceStep(cs, remain, (currentOps._1 :+ cand, currentOps._2 ++ dropped)) + boxesToAdd += cand + boxesToDrop ++= dropped + replaceStep(cs, remain) } } - val (toAdd, toDrop) = replaceStep(bigBoxes, sorted, (Seq(), Seq())) - if (toAdd.nonEmpty) { - val compactedBoxes = bsr.boxes.filter(b => !toDrop.contains(b)) ++ toAdd + replaceStep(bigBoxes, sorted) + if (boxesToAdd.nonEmpty) { + val compactedBoxes = bsr.boxes.filter(b => !boxesToDrop.contains(b)) ++ boxesToAdd calcChange(compactedBoxes, targetBalance, targetAssets) .mapRight(changeBoxes => BoxSelectionResult(compactedBoxes, changeBoxes)) } else { @@ -170,5 +175,5 @@ class ReplaceCompactCollectBoxSelector(maxInputs: Int, } object ReplaceCompactCollectBoxSelector { - final case class MaxInputsExceededError(message: String) extends BoxSelectionError + final case class MaxInputsExceededError(message: String) extends BoxSelectionError } diff --git a/src/main/resources/api/openapi.yaml b/src/main/resources/api/openapi.yaml index 9e991dbc31..8af3cddf33 100644 --- a/src/main/resources/api/openapi.yaml +++ b/src/main/resources/api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.2" info: - version: "5.0.1" + version: "5.0.2" title: Ergo Node API description: API docs for Ergo Node. Models are shared between all Ergo products contact: diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 9588f2eb1c..1d29375973 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -408,7 +408,7 @@ scorex { nodeName = "ergo-node" # Network protocol version to be sent in handshakes - appVersion = 5.0.1 + appVersion = 5.0.2 # Network agent name. May contain information about client code # stack, starting from core code-base up to the end graphical interface. diff --git a/src/main/resources/mainnet.conf b/src/main/resources/mainnet.conf index 5972a679c8..59b8dff5c1 100644 --- a/src/main/resources/mainnet.conf +++ b/src/main/resources/mainnet.conf @@ -78,7 +78,7 @@ scorex { network { magicBytes = [1, 0, 2, 4] bindAddress = "0.0.0.0:9030" - nodeName = "ergo-mainnet-5.0.1" + nodeName = "ergo-mainnet-5.0.2" nodeName = ${?NODENAME} knownPeers = [ "213.239.193.208:9030", diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala index 5b94f72c72..059807554f 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala @@ -256,7 +256,7 @@ trait ErgoWalletSupport extends ScorexLogging { } val inputBoxes = selectionResult.boxes.toIndexedSeq new UnsignedErgoTransaction( - inputBoxes.map(_.box.id).map(id => new UnsignedInput(id)), + inputBoxes.map(tx => new UnsignedInput(tx.box.id)), dataInputs, (payTo ++ changeBoxCandidates).toIndexedSeq ) diff --git a/src/main/scala/scorex/core/api/http/ApiRejectionHandler.scala b/src/main/scala/scorex/core/api/http/ApiRejectionHandler.scala index d0427015c0..628316b301 100644 --- a/src/main/scala/scorex/core/api/http/ApiRejectionHandler.scala +++ b/src/main/scala/scorex/core/api/http/ApiRejectionHandler.scala @@ -31,6 +31,6 @@ object ApiRejectionHandler { } .handle { case ValidationRejection(msg, _) => ApiError.BadRequest(msg) } .handle { case x => ApiError.InternalError(s"Unhandled rejection: $x") } - .handleNotFound { ApiError.NotExists("The requested resource could not be found.") } + .handleNotFound { ApiError.BadRequest("The requested resource/endpoint could not be found.") } .result() }