diff --git a/FAQ.md b/FAQ.md index d69d1f0626..a041b2a8dc 100644 --- a/FAQ.md +++ b/FAQ.md @@ -44,12 +44,16 @@ The details of the Ergo emission schedule and monetary supply can be found in th * Website: https://ergoplatform.org/ -* Twitter: https://twitter.com/ergoplatformorg +* ErgoDocs: https://docs.ergoplatform.com -* Wiki: https://github.com/ergoplatform/ergo/wiki +* Twitter: https://twitter.com/Ergo_Platform + +* Telegram: https://t.me/ergoplatform +* Ecosystem: https://sigmaverse.io +* * Github: https://github.com/ergoplatform/ergo * Documents: https://ergoplatform.org/en/documents/ -* Telegram: https://t.me/ergoplatform +* Wiki: https://github.com/ergoplatform/ergo/wiki diff --git a/README.md b/README.md index d145829dc5..a296bdf8b2 100644 --- a/README.md +++ b/README.md @@ -1,68 +1,96 @@ # Ergo -This repository contains the reference implementation of the -Ergo Platform protocol, which is an alternative to -the [Bitcoin protocol](https://bitcoin.org/bitcoin.pdf). +Welcome to the official repository for the [Ergo Platform](https://ergoplatform.org/). This repository contains the reference client, also known as the node, for Ergo. Ergo is a cryptocurrency protocol that has been designed to offer a secure environment for peer-to-peer transactions. It supports programmable scarce money (Ergo) and a wide range of financial tools. -Ergo Platform website: [https://ergoplatform.org/](https://ergoplatform.org/) +The reference client is primarily written in Scala. While certain components of the protocol are implemented in other languages (for instance, [sigma-rust](https://github.com/ergoplatform/sigma-rust) is a Rust-based implementation of the ErgoScript cryptocurrency scripting language), the reference client provides the most complete and comprehensive implementation of the Ergo protocol. -## Differences from Bitcoin +## Key Features of Ergo -* Powerful contracts in the multi-stage extended UTXO model (see [ErgoScript whitepaper](https://ergoplatform.org/docs/ErgoScript.pdf)) -* Memory-hard Proof-of-Work function [Autolykos2](https://docs.ergoplatform.com/ErgoPow.pdf) -* Support for stateless clients (asymmetric, based on [https://eprint.iacr.org/2016/994](https://eprint.iacr.org/2016/994)), -[NiPoPoWs](https://eprint.iacr.org/2017/963.pdf), hybrid modes -* [Alternative transactional language](https://github.com/ScorexFoundation/sigmastate-interpreter), which is more powerful than Bitcoin Script but also safe against -heavy validation attacks -* Alternative fee model with [mandatory storage-rent component](https://fc18.ifca.ai/bitcoin/papers/bitcoin18-final18.pdf ) +Ergo, while sharing some commonalities with Bitcoin as a UTXO Proof-of-Work cryptocurrency, stands out due to its unique design and features. It has been built from the ground up, introducing several innovative elements: + +* **ErgoScript**: A powerful contract language in the multi-stage extended UTXO model. More details can be found in the [ErgoScript whitepaper](https://ergoplatform.org/docs/ErgoScript.pdf). +* **Autolykos2**: A memory-hard Proof-of-Work function, providing enhanced security. Learn more about it [here](https://docs.ergoplatform.com/ErgoPow.pdf). +* Support for Stateless Clients: Ergo supports asymmetric stateless clients, based on [this paper](https://eprint.iacr.org/2016/994), and includes features like NiPoPoWs and hybrid modes. +* **Advanced Transactional Language**: Ergo introduces an [alternative transactional language](https://github.com/ScorexFoundation/sigmastate-interpreter) that is more powerful than Bitcoin Script and is designed to be safe against heavy validation attacks. +* **Innovative Fee Model**: Ergo implements an alternative fee model with a [mandatory storage-rent component](https://fc18.ifca.ai/bitcoin/papers/bitcoin18-final18.pdf ) (also known as demurrage). ## Specifications -A [White Paper](https://ergoplatform.org/docs/whitepaper.pdf) with a brief description is available. A Yellow Paper with detailed specification is underway and will be available shortly. At the moment, there are [drafts of the Yellow Paper](https://github.com/ergoplatform/ergo/tree/master/papers/yellow) available, -and currently the reference implementation code should be considered as the specification. +* [white paper](https://ergoplatform.org/docs/whitepaper.pdf) - a brief description of the protocol +* [ErgoScript white paper](https://ergoplatform.org/docs/ErgoScript.pdf) - describes ErgoScript, a Cryptocurrency Scripting Language Supporting Noninteractive Zero-Knowledge Proofs used in Ergo + +More papers can be found at [docs.ergoplatform.com/documents](https://docs.ergoplatform.com/documents/). + +## Security Assumptions + +The Ergo client operates under certain assumptions about its environment: + +* The execution environment is trusted. Although the seed is stored in an encrypted file, and the client's wallet attempts to purge the secret key from memory as soon as it is no longer needed, the client does not have defenses against side-channel attacks, memory scans, etc. +* Clocks are expected to be synchronized to a reasonable degree. If a block's timestamp is more than 20 minutes into the future, the block will be temporarily rejected. The client does not utilize NTP or other time synchronization protocols. + +## Building and Running the Node and UI + +For instructions on how to build and run the node and UI, refer to the [official documentation](https://docs.ergoplatform.com/node/install/). + +By default, the node processes all blocks from the genesis block. However, there are other options available that may be more suitable for hardware with limited resources. + +* **Bootstrapping with a UTXO set snapshot:** This works similarly to Ethereum's snap-sync. The node first downloads a UTXO set snapshot from a secure point in the past, then downloads blocks following the UTXO set snapshot and applies them to the set. For more details and security proofs, refer to the ["Multi-mode cryptocurrency systems" paper](https://eprint.iacr.org/2018/129.pdf). To enable this feature add the following to your configuration file: +``` + +ergo { + ... + node.utxoBootstrap = true + ... +} +``` + +* The UTXO set snapshot bootstrapping can be further optimized by combining it with NiPoPoW (Non-Interactive Proofs of Proof-of-Work). This method allows for syncing the headers-chain in logarithmic time, as opposed to the linear time required by the standard SPV sync for headers. For more details, refer to the [NiPoPoW paper](https://eprint.iacr.org/2017/963.pdf). +``` + +ergo{ + ... + node.nipopow.nipopowBootstrap = true + node.utxo.utxoBootstrap = true + ... +} +``` + +* The stateless mode provides full-node security without the need to hold the entire UTXO set. This is achieved through the methods detailed in the ["Improving Authenticated Dynamic Dictionaries, with Applications to Cryptocurrencies" paper](https://eprint.iacr.org/2016/994.pdf). In this mode, it's possible to download and validate an arbitrary-sized suffix of the blockchain. Here's an example of how to configure this mode: -## Security assumptions +``` +ergo { + ... + node.stateType = "digest" + node.blocksToKeep = 2160 # store and process last three days only + node.nipopow.nipopowBootstrap = true # compatible with NiPoPoWs + ... +} +``` -This client relies on some assumptions in regards with its environment: -* execution environment is trusted. While seed is stored in encrypted files, and the client's - wallet tries to remove secret key from memory as soon as possible when it is not needed, the - client has no protection from side-channel attacks, memory scans etc. -* clocks should be more or less synchronized. If timestamp of a block is more than 20 minutes - in future, the block will be temporarily rejected. The client does not use NTP or other time - syncing protocols. +For more detailed information on different modes of node operation, please visit [docs.ergoplatform.com/node/modes](https://docs.ergoplatform.com/node/modes). -## Building and Running Node and UI +## Testing Procedures -See [documentation](https://docs.ergoplatform.com/node/install/) +Ergo utilizes three types of tests: -## Testing +1) Unit and property tests: These can be run using the `sbt test` command. +2) Integration tests: These tests require Docker to be installed. Run them with the `sudo sbt it:test` command. +3) Bootstrapping tests: These tests are time-consuming as they verify that the node is syncing with the main network in various regimes. Docker is also required for these tests. Run them with the `sudo sbt it2:test` command. -There are three kinds of tests: +## Setting up the Project in an IDE -1) Unit and property tests, run them with `sbt test` command. -2) Integration tests, they require for Docker to be installed, then run `sudo sbt it:test`. -3) Bootstrapping tests, very slow as they are checking that the node is indeed catching up with the main network in -different regimes, they require for Docker too, run as `sudo sbt it2:test`. +You can use either [IntelliJ IDEA](https://www.jetbrains.com/idea/) (Community or Ultimate edition) or [VSCode](https://code.visualstudio.com/) with the [Metals](https://scalameta.org/metals/) extension. -## Open project in IDE +Ensure that the project can be built with sbt before opening it in an IDE. You may need to resolve any dependency errors first. -Your can use [IntelliJ IDEA](https://www.jetbrains.com/idea/) (Community or Ultimate edition) or -[VSCode](https://code.visualstudio.com/) + [Metals](https://scalameta.org/metals/). -Before opening the project in IDE make sure it can be built with sbt. -You may need to fix dependency resolution errors first. +To open the project in IntelliJ IDEA, select File / Open and navigate to the project folder. This will initiate the Project Import Wizard, which uses the SBT configuration (build.sbt file) to generate the project configuration files for IDEA. You can view the project configuration in the `File / Project Structure...` dialog. If the import is successful, you should be able to compile the project in the IDE. -After that you can open the project folder in Idea (File / Open) -which will run Project Import Wizard. The wizard will use SBT configuration -(build.sbt file) to generate Idea's project configuration files. -You can open `File / Project Structure...` dialog to see project configuration. -If everything is successful you can compile the project in IDE. +## Contributing to Ergo -## Contributions +Ergo is an open-source project and we welcome contributions from developers and testers! Join the discussion on [Ergo Discord](https://discord.gg/kj7s7nb) in the #development channel and check out our [Contributing documentation](https://docs.ergoplatform.com/contribute/). -Ergo is open-source and open movement, always in need for testers and developers! Please feel free -to discuss development in [Ergo Discord](https://discord.gg/kj7s7nb), #development channel. +## Frequently Asked Questions -## FAQ -[Frequently Asked Questions](FAQ.md) +For common queries, please refer to our [Frequently Asked Questions](FAQ.md) page. diff --git a/build.sbt b/build.sbt index e880b0fa32..a72c075a74 100644 --- a/build.sbt +++ b/build.sbt @@ -37,7 +37,7 @@ val circeVersion = "0.13.0" val akkaVersion = "2.6.10" val akkaHttpVersion = "10.2.4" -val sigmaStateVersion = "5.0.8" +val sigmaStateVersion = "5.0.12" // for testing current sigmastate build (see sigmastate-ergo-it jenkins job) val effectiveSigmaStateVersion = Option(System.getenv().get("SIGMASTATE_VERSION")).getOrElse(sigmaStateVersion) diff --git a/ergo-wallet/src/main/java/org/ergoplatform/wallet/interface4j/crypto/ErgoUnsafeProver.java b/ergo-wallet/src/main/java/org/ergoplatform/wallet/interface4j/crypto/ErgoUnsafeProver.java index 4493e490e5..f12f4cb457 100644 --- a/ergo-wallet/src/main/java/org/ergoplatform/wallet/interface4j/crypto/ErgoUnsafeProver.java +++ b/ergo-wallet/src/main/java/org/ergoplatform/wallet/interface4j/crypto/ErgoUnsafeProver.java @@ -3,7 +3,7 @@ import org.ergoplatform.ErgoLikeTransaction; import org.ergoplatform.UnsignedErgoLikeTransaction; import scala.collection.JavaConverters; -import sigmastate.basics.DLogProtocol; +import sigmastate.crypto.DLogProtocol; import java.util.Map; /** diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/contracts/ReemissionContracts.scala b/ergo-wallet/src/main/scala/org/ergoplatform/contracts/ReemissionContracts.scala index 55a70b4016..7569b8c00c 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/contracts/ReemissionContracts.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/contracts/ReemissionContracts.scala @@ -8,7 +8,7 @@ import org.ergoplatform.{Height, MinerPubkey, Outputs, Self} import sigmastate.Values.{ByteArrayConstant, ErgoTree, IntConstant, LongConstant, SigmaPropValue, Value} import sigmastate.utxo._ import sigmastate._ -import special.collection.Coll +import sigma.Coll /** * Container for re-emission related contracts. Contains re-emission contract and pay-to-reemission contract. diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ErgoBoxAssetExtractor.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ErgoBoxAssetExtractor.scala index 8c2fdf5384..34dcdc1c74 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ErgoBoxAssetExtractor.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ErgoBoxAssetExtractor.scala @@ -2,7 +2,7 @@ package org.ergoplatform.wallet.boxes import java7.compat.Math import org.ergoplatform.ErgoBoxCandidate -import special.collection.Extensions._ +import sigma.Extensions._ import scala.collection.compat.immutable.ArraySeq import scala.collection.mutable diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/TrackedBox.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/TrackedBox.scala index 2cfec5ad29..472b54e9f8 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/TrackedBox.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/TrackedBox.scala @@ -8,7 +8,7 @@ import org.ergoplatform.wallet.serialization.ErgoWalletSerializer import org.ergoplatform.{ErgoBox, ErgoBoxAssets, ErgoLikeTransaction} import scorex.util.serialization.{Reader, Writer} import scorex.util.{ModifierId, bytesToId, idToBytes} -import special.collection.Extensions._ +import sigma.Extensions._ /** * A box tracked by a wallet that contains Ergo box itself as well as diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/crypto/ErgoSignature.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/crypto/ErgoSignature.scala index 03f144ea2d..430fc8c894 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/crypto/ErgoSignature.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/crypto/ErgoSignature.scala @@ -3,7 +3,7 @@ package org.ergoplatform.wallet.crypto import org.bouncycastle.util.BigIntegers import scorex.crypto.hash.Blake2b256 import scorex.util.encode.Base16 -import sigmastate.basics.CryptoConstants +import sigmastate.crypto.CryptoConstants import sigmastate.serialization.GroupElementSerializer import scala.annotation.tailrec diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoInterpreter.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoInterpreter.scala index fbed24ecfb..bdd423615f 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoInterpreter.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoInterpreter.scala @@ -1,14 +1,14 @@ package org.ergoplatform.wallet.interpreter import org.ergoplatform.ErgoLikeContext.Height -import org.ergoplatform.sdk.wallet.protocol.context.ErgoLikeParameters +import org.ergoplatform.sdk.BlockchainParameters import org.ergoplatform.wallet.protocol.Constants import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, ErgoLikeContext, ErgoLikeInterpreter} -import scorex.crypto.authds.ADDigest import scorex.util.ScorexLogging import sigmastate.Values.ErgoTree import sigmastate.interpreter.Interpreter.{ScriptEnv, VerificationResult} import sigmastate.{AvlTreeData, AvlTreeFlags} +import sigma.Coll import scala.util.Try @@ -18,7 +18,7 @@ import scala.util.Try * * @param params - current values of adjustable blockchain settings */ -class ErgoInterpreter(params: ErgoLikeParameters) +class ErgoInterpreter(params: BlockchainParameters) extends ErgoLikeInterpreter with ScorexLogging { /** Override default logging for all Ergo interpreters. */ @@ -96,11 +96,11 @@ object ErgoInterpreter { val interpreterInitCost = 10000 /** Creates an interpreter with the given parameters. */ - def apply(params: ErgoLikeParameters): ErgoInterpreter = + def apply(params: BlockchainParameters): ErgoInterpreter = new ErgoInterpreter(params) /** Create [[AvlTreeData]] with the given digest and all operations enabled. */ - def avlTreeFromDigest(digest: ADDigest): AvlTreeData = { + def avlTreeFromDigest(digest: Coll[Byte]): AvlTreeData = { val flags = AvlTreeFlags(insertAllowed = true, updateAllowed = true, removeAllowed = true) AvlTreeData(digest, flags, Constants.HashLength) } diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreter.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreter.scala index 4d1d62ed6f..62732ab788 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreter.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreter.scala @@ -1,19 +1,18 @@ package org.ergoplatform.wallet.interpreter import org.ergoplatform._ +import org.ergoplatform.sdk.BlockchainParameters import org.ergoplatform.sdk.utils.ArithUtils.{addExact, multiplyExact} -import org.ergoplatform.sdk.wallet.protocol.context.{ErgoLikeParameters, ErgoLikeStateContext} +import org.ergoplatform.sdk.wallet.protocol.context.BlockchainStateContext import org.ergoplatform.sdk.wallet.secrets.{ExtendedPublicKey, ExtendedSecretKey, SecretKey} import org.ergoplatform.validation.{SigmaValidationSettings, ValidationRules} import org.ergoplatform.wallet.boxes.ErgoBoxAssetExtractor -import scorex.crypto.authds.ADDigest import scorex.util.encode.Base16 import sigmastate.AvlTreeData import sigmastate.Values.SigmaBoolean -import sigmastate.basics.SigmaProtocolPrivateInput +import sigmastate.crypto.SigmaProtocolPrivateInput import sigmastate.interpreter.{ContextExtension, ProverInterpreter} -import special.collection.Coll -import special.sigma.{Header, PreHeader} +import sigma.{Coll, Header, PreHeader} import java.util import scala.util.{Failure, Success, Try} @@ -39,14 +38,14 @@ import scala.util.{Failure, Success, Try} * (to not to recompute them) */ class ErgoProvingInterpreter(val secretKeys: IndexedSeq[SecretKey], - params: ErgoLikeParameters, + params: BlockchainParameters, val cachedHdPubKeysOpt: Option[IndexedSeq[ExtendedPublicKey]] = None) extends ErgoInterpreter(params) with ProverInterpreter { /** * Interpreter's secrets, in form of sigma protocols private inputs */ - val secrets: IndexedSeq[SigmaProtocolPrivateInput[_, _]] = secretKeys.map(_.privateInput) + val secrets: IndexedSeq[SigmaProtocolPrivateInput[_]] = secretKeys.map(_.privateInput) /** * Only secrets corresponding to hierarchical deterministic scheme (BIP-32 impl) @@ -93,14 +92,14 @@ class ErgoProvingInterpreter(val secretKeys: IndexedSeq[SecretKey], * @param newParams - updated parameters * @return modified prover */ - def withNewParameters(newParams: ErgoLikeParameters): ErgoProvingInterpreter = { + def withNewParameters(newParams: BlockchainParameters): ErgoProvingInterpreter = { new ErgoProvingInterpreter(this.secretKeys, newParams, this.cachedHdPubKeysOpt) } def signInputs(unsignedTx: UnsignedErgoLikeTransaction, boxesToSpend: IndexedSeq[ErgoBox], dataBoxes: IndexedSeq[ErgoBox], - stateContext: ErgoLikeStateContext, + stateContext: BlockchainStateContext, txHints: TransactionHintsBag): Try[(IndexedSeq[Input], Long)] = { if (unsignedTx.inputs.length != boxesToSpend.length) { Failure(new Exception("Not enough boxes to spend")) @@ -164,7 +163,7 @@ class ErgoProvingInterpreter(val secretKeys: IndexedSeq[SecretKey], def sign(unsignedTx: UnsignedErgoLikeTransaction, boxesToSpend: IndexedSeq[ErgoBox], dataBoxes: IndexedSeq[ErgoBox], - stateContext: ErgoLikeStateContext, + stateContext: BlockchainStateContext, txHints: TransactionHintsBag = TransactionHintsBag.empty): Try[ErgoLikeTransaction] = { val signedInputs: Try[(IndexedSeq[Input], Long)] = @@ -190,7 +189,7 @@ class ErgoProvingInterpreter(val secretKeys: IndexedSeq[SecretKey], def generateCommitmentsFor(unsignedTx: UnsignedErgoLikeTransaction, boxesToSpend: IndexedSeq[ErgoBox], dataBoxes: IndexedSeq[ErgoBox], - stateContext: ErgoLikeStateContext): Try[TransactionHintsBag] = Try { + stateContext: BlockchainStateContext): Try[TransactionHintsBag] = Try { val inputCmts = unsignedTx.inputs.zipWithIndex.map { case (unsignedInput, inpIndex) => val inputBox = boxesToSpend(inpIndex) @@ -230,7 +229,7 @@ class ErgoProvingInterpreter(val secretKeys: IndexedSeq[SecretKey], def bagForTransaction(tx: ErgoLikeTransaction, boxesToSpend: IndexedSeq[ErgoBox], dataBoxes: IndexedSeq[ErgoBox], - stateContext: ErgoLikeStateContext, + stateContext: BlockchainStateContext, realSecretsToExtract: Seq[SigmaBoolean], simulatedSecretsToExtract: Seq[SigmaBoolean]): TransactionHintsBag = { val augmentedInputs = tx.inputs.zipWithIndex.zip(boxesToSpend) @@ -262,11 +261,11 @@ class ErgoProvingInterpreter(val secretKeys: IndexedSeq[SecretKey], object ErgoProvingInterpreter { def apply(secrets: IndexedSeq[SecretKey], - params: ErgoLikeParameters): ErgoProvingInterpreter = + params: BlockchainParameters): ErgoProvingInterpreter = new ErgoProvingInterpreter(secrets, params) def apply(rootSecret: ExtendedSecretKey, - params: ErgoLikeParameters): ErgoProvingInterpreter = + params: BlockchainParameters): ErgoProvingInterpreter = new ErgoProvingInterpreter(IndexedSeq(rootSecret), params) } diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoUnsafeProver.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoUnsafeProver.scala index ad1f780404..c2b763cb65 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoUnsafeProver.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoUnsafeProver.scala @@ -2,7 +2,7 @@ package org.ergoplatform.wallet.interpreter import org.ergoplatform.{ErgoLikeTransaction, Input, UnsignedErgoLikeTransaction} import scorex.util.encode.Base16 -import sigmastate.basics.DLogProtocol.DLogProverInput +import sigmastate.crypto.DLogProtocol.DLogProverInput import sigmastate.interpreter.{ContextExtension, ProverResult} /** diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/serialization/JsonCodecsWrapper.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/serialization/JsonCodecsWrapper.scala index 4c6eda7818..fe9ef84fe2 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/serialization/JsonCodecsWrapper.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/serialization/JsonCodecsWrapper.scala @@ -1,6 +1,6 @@ package org.ergoplatform.wallet.serialization -import org.ergoplatform.JsonCodecs +import org.ergoplatform.sdk.JsonCodecs /** * JSON Codecs provided as singleton package, not trait. diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/transactions/TransactionBuilder.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/transactions/TransactionBuilder.scala index 6ad66b5423..745002eacf 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/transactions/TransactionBuilder.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/transactions/TransactionBuilder.scala @@ -10,8 +10,8 @@ import scorex.util.{ModifierId, bytesToId} import sigmastate.eval.Extensions._ import sigmastate.eval._ import sigmastate.utils.Extensions._ -import special.collection.Coll -import special.collection.Extensions._ +import sigma.Coll +import sigma.Extensions._ import scala.collection.JavaConverters._ import scala.util.Try diff --git a/ergo-wallet/src/test/java/org/ergoplatform/wallet/CreateTransactionDemo.java b/ergo-wallet/src/test/java/org/ergoplatform/wallet/CreateTransactionDemo.java index 11ac65f6b0..70b39eea0e 100644 --- a/ergo-wallet/src/test/java/org/ergoplatform/wallet/CreateTransactionDemo.java +++ b/ergo-wallet/src/test/java/org/ergoplatform/wallet/CreateTransactionDemo.java @@ -8,7 +8,7 @@ import org.ergoplatform.wallet.transactions.TransactionBuilder; import org.ergoplatform.wallet.transactions.TransactionBuilder.Payment; import scorex.util.Random; -import sigmastate.basics.DLogProtocol; +import sigmastate.crypto.DLogProtocol; import java.util.ArrayList; import java.util.HashMap; diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelectorSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelectorSpec.scala index 311275b06f..f34f050501 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelectorSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelectorSpec.scala @@ -16,7 +16,7 @@ import sigmastate.Values.SigmaPropValue import sigmastate.eval.Extensions._ import sigmastate.helpers.TestingHelpers._ import sigmastate.utils.Extensions._ -import special.collection.Extensions._ +import sigma.Extensions._ import scala.util.Random diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/crypto/ErgoSignatureSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/crypto/ErgoSignatureSpec.scala index 42993ec9df..5c35807d5e 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/crypto/ErgoSignatureSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/crypto/ErgoSignatureSpec.scala @@ -4,7 +4,7 @@ import org.ergoplatform.wallet.utils.Generators import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import scorex.util.Random -import sigmastate.basics.DLogProtocol.DLogProverInput +import sigmastate.crypto.DLogProtocol.DLogProverInput class ErgoSignatureSpec extends AnyPropSpec with Matchers with Generators { diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreterSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreterSpec.scala index 0d96c4c8cc..e821987d01 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreterSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreterSpec.scala @@ -9,6 +9,7 @@ import org.scalatest.matchers.should.Matchers import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import scorex.util.{ModifierId, Random} import scorex.util.encode.Base16 +import sigma.Colls import sigmastate.CTHRESHOLD import sigmastate.Values.{GroupElementConstant, SigmaBoolean} import sigmastate.interpreter.{ContextExtension, HintsBag} diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/InterpreterSpecCommon.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/InterpreterSpecCommon.scala index d4af16e52d..5f797a27d2 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/InterpreterSpecCommon.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/InterpreterSpecCommon.scala @@ -1,16 +1,16 @@ package org.ergoplatform.wallet.interpreter -import org.ergoplatform.sdk.wallet.protocol.context.{ErgoLikeParameters, ErgoLikeStateContext} -import scorex.crypto.authds.ADDigest +import org.ergoplatform.sdk.BlockchainParameters +import org.ergoplatform.sdk.wallet.protocol.context.BlockchainStateContext import scorex.util.encode.Base16 -import sigmastate.basics.CryptoConstants -import sigmastate.eval.{CGroupElement, CPreHeader, Colls} -import special.collection.Coll -import special.sigma.{Header, PreHeader} +import sigmastate.crypto.CryptoConstants +import sigmastate.eval.Extensions.ArrayOps +import sigmastate.eval.{CGroupElement, CPreHeader} +import sigma.{Coll, Colls, Header, PreHeader} trait InterpreterSpecCommon { - protected val parameters = new ErgoLikeParameters { + protected val parameters = new BlockchainParameters { override def storageFeeFactor: Int = 1250000 @@ -35,13 +35,14 @@ trait InterpreterSpecCommon { override def blockVersion: Byte = 1 } - protected val stateContext = new ErgoLikeStateContext { + protected val stateContext = new BlockchainStateContext { override def sigmaLastHeaders: Coll[Header] = Colls.emptyColl - override def previousStateDigest: ADDigest = - ADDigest @@ Base16.decode("a5df145d41ab15a01e0cd3ffbab046f0d029e5412293072ad0f5827428589b9302") + override def previousStateDigest: Coll[Byte] = + Base16.decode("a5df145d41ab15a01e0cd3ffbab046f0d029e5412293072ad0f5827428589b9302") .getOrElse(throw new Error(s"Failed to parse genesisStateDigest")) + .toColl override def sigmaPreHeader: PreHeader = CPreHeader( version = 0, diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/utils/Generators.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/utils/Generators.scala index fc8d6d3ab3..8ba31c3c27 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/utils/Generators.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/utils/Generators.scala @@ -13,7 +13,7 @@ import org.scalacheck.{Arbitrary, Gen} import scorex.crypto.authds.ADKey import scorex.util._ import sigmastate.Values.{ByteArrayConstant, CollectionConstant, ErgoTree, EvaluatedValue, FalseLeaf, TrueLeaf} -import sigmastate.basics.DLogProtocol.ProveDlog +import sigmastate.crypto.DLogProtocol.ProveDlog import sigmastate.crypto.CryptoFacade.SecretKeyLength import sigmastate.eval.Extensions._ import sigmastate.eval._ diff --git a/src/it/scala/org/ergoplatform/it/WalletSpec.scala b/src/it/scala/org/ergoplatform/it/WalletSpec.scala index a3c6b4012a..3d22140ea0 100644 --- a/src/it/scala/org/ergoplatform/it/WalletSpec.scala +++ b/src/it/scala/org/ergoplatform/it/WalletSpec.scala @@ -20,6 +20,7 @@ import org.ergoplatform.{ErgoBox, P2PKAddress} import org.scalatest.wordspec.AsyncWordSpec import scorex.util.ModifierId import scorex.util.encode.Base16 +import sigma.Colls import sigmastate.Values.{ErgoTree, TrueLeaf} import scala.concurrent.ExecutionContext diff --git a/src/main/resources/.well-known/ai-plugin.json b/src/main/resources/.well-known/ai-plugin.json new file mode 100644 index 0000000000..5100ce83dd --- /dev/null +++ b/src/main/resources/.well-known/ai-plugin.json @@ -0,0 +1,18 @@ +{ + "schema_version": "v1", + "name_for_human": "Ergo Node Plugin (no auth)", + "name_for_model": "ergonode", + "description_for_human": "Plugin for interacting with Ergo node.", + "description_for_model": "Specification of Ergo Node API for ChatGPT plugin.\n The following endpoints supported \n - /blocks/chainSlice - Get headers in a specified range of heights\n - /info - Get the basic information about the status of Ergo Node. \n - /transactions/unconfirmed/byTransactionId - Get unconfirmed transaction from the mempool\n - /transactions/poolHistogram - Get histogram (waittime, (n_trans, sum(fee)) for transactions in mempool.\n - /peers/connected - Get a list of current connected peers\n - /peers/blacklisted - Get a list of blacklisted peers\n - /utils/address - Check address validity\n - /blockchain/indexedHeight - Get current indexed block height. (The indexer has processed all blocks up to this height.)\n - /blockchain/transaction/byId - Retrieve a transaction by its id\n - /blockchain/transaction/byAddress - Retrieve a list of transactions by their associated address\n - /blockchain/box/byId - Retrieve a box by its id\n - /blockchain/box/byAddress - Retrieve boxes by their associated \n - /blockchain/box/unspent/byAddress - Retrieve unspent boxes by their associated address\n - /blockchain/token/byId - Retrieve minting information about a token\n - /blockchain/balanceForAddress - Retrieve balance information of an Ergo address.", + "auth": { + "type": "none" + }, + "api": { + "type": "openapi", + "url": "http://localhost:9053/openapi.yaml", + "is_user_authenticated": false + }, + "logo_url": "https://cryptologos.cc/logos/ergo-erg-logo.png", + "contact_email": "team@ergoplatform.org", + "legal_info_url": "https://ergoplatform.org/en/legal/" +} diff --git a/src/main/resources/api/openapi-ai.yaml b/src/main/resources/api/openapi-ai.yaml new file mode 100644 index 0000000000..392309a6d1 --- /dev/null +++ b/src/main/resources/api/openapi-ai.yaml @@ -0,0 +1,1482 @@ +openapi: "3.0.2" + +info: + version: "5.0.14" + title: Ergo Node API + description: Specification of Ergo Node API for ChatGPT plugin. + The following endpoints supported + - /blocks/chainSlice - Get headers in a specified range of heights + - /info - Get the basic information about the status of Ergo Node. + - /transactions/unconfirmed/byTransactionId - Get unconfirmed transaction from the mempool + - /transactions/poolHistogram - Get histogram (waittime, (n_trans, sum(fee)) for transactions in mempool. + - /blockchain/indexedHeight - Get current indexed block height. (The indexer has processed all blocks up to this height.) + - /blockchain/transaction/byId - Retrieve a transaction by its id + - /blockchain/transaction/byAddress - Retrieve a list of transactions by their associated address + - /blockchain/box/byId - Retrieve a box by its id + - /blockchain/box/byAddress - Retrieve boxes by their associated + - /blockchain/box/unspent/byAddress - Retrieve unspent boxes by their associated address + - /blockchain/token/byId - Retrieve minting information about a token + - /blockchain/balanceForAddress - Retrieve balance information of an Ergo address. + +servers: + - url: http://localhost:9052 + description: Ergo full node API (testnet). + - url: http://localhost:9053 + description: Ergo full node API (mainnet). + +components: + schemas: + # Objects + ErgoTransactionInput: + type: object + required: + - boxId + - spendingProof + properties: + boxId: + $ref: '#/components/schemas/TransactionBoxId' + spendingProof: + $ref: '#/components/schemas/SpendingProof' + + ErgoTransactionDataInput: + type: object + required: + - boxId + properties: + boxId: + $ref: '#/components/schemas/TransactionBoxId' + + SpendingProof: + description: Spending proof for transaction input + type: object + required: + - proofBytes + - extension + properties: + proofBytes: + $ref: '#/components/schemas/SpendingProofBytes' + extension: + type: object + description: Variables to be put into context + additionalProperties: + $ref: '#/components/schemas/SValue' + example: + '1': 'a2aed72ff1b139f35d1ad2938cb44c9848a34d4dcfd6d8ab717ebde40a7304f2541cf628ffc8b5c496e6161eba3f169c6dd440704b1719e0' + + ErgoTransactionOutput: + type: object + required: + - value + - ergoTree + - additionalRegisters + - creationHeight + properties: + boxId: + $ref: '#/components/schemas/TransactionBoxId' + value: + description: Amount of Ergo token + type: integer + format: int64 + minimum: 0 + example: 147 + ergoTree: + $ref: '#/components/schemas/ErgoTree' + creationHeight: + description: Height the output was created at + type: integer + format: int32 + example: 9149 + assets: + description: Assets list in the transaction + type: array + items: + $ref: '#/components/schemas/Asset' + additionalRegisters: + $ref: '#/components/schemas/Registers' + transactionId: + $ref: '#/components/schemas/TransactionId' + index: + description: Index in the transaction outputs + type: integer + format: int32 + + BalanceInfo: + type: object + description: Represents a balance information (e.g. for an address) + required: + - nanoErgs + - tokens + properties: + nanoErgs: + type: integer + format: int64 + description: Balance of nanoERGs + tokens: + type: array + description: List of assets (aks tokens) with balances + items: + type: object + properties: + tokenId: + $ref: '#/components/schemas/ModifierId' + description: Identifier of the asset (aka token) + amount: + type: integer + format: int64 + description: Amount of the asset (aka token) + decimals: + type: integer + description: Number of decimals of the token + name: + type: string + description: Name of the token, if any + + IndexedErgoBox: + type: object + description: Box indexed with extra information + required: + - box + - confirmationsNum + - address + - creationTransaction + - spendingTransaction + - spendingHeight + - inclusionHeight + - spent + - globalIndex + properties: + box: + $ref: '#/components/schemas/ErgoTransactionOutput' + confirmationsNum: + description: Number of confirmations, if the box is included into the blockchain + type: integer + format: int32 + minimum: 0 + example: 147 + nullable: true + address: + $ref: '#/components/schemas/ErgoAddress' + creationTransaction: + description: Transaction which created the box + $ref: '#/components/schemas/ModifierId' + spendingTransaction: + description: Transaction which created the box + nullable: true + $ref: '#/components/schemas/ModifierId' + spendingHeight: + description: The height the box was spent at + type: integer + format: int32 + minimum: 0 + example: 147 + nullable: true + inclusionHeight: + description: The height the transaction containing the box was included in a block at + type: integer + format: int32 + minimum: 0 + example: 147 + spent: + description: A flag signalling whether the box was spent + type: boolean + example: false + globalIndex: + description: Global index of the output in the blockchain + type: integer + format: int64 + minimum: 0 + example: 83927 + + IndexedToken: + type: object + description: Token indexed with extra information + required: + - id + - boxId + - emissionAmount + - name + - description + - decimals + properties: + id: + description: Id of the token + $ref: '#/components/schemas/ModifierId' + boxId: + description: Id of the box that created the token + $ref: '#/components/schemas/ModifierId' + emissionAmount: + description: The total supply of the token + type: integer + format: int64 + minimum: 1 + example: 3500000 + name: + description: The name of the token + type: string + description: + description: The description of the token + type: string + decimals: + description: The number of decimals the token supports + type: integer + format: int32 + minimum: 0 + example: 8 + + ErgoTransaction: + type: object + description: ErgoTransaction is an atomic operation which changes UTXO state. + required: + - inputs + - dataInputs + - outputs + properties: + id: + description: Id of the transaction + $ref: '#/components/schemas/TransactionId' + inputs: + description: Inputs, that will be spent by this transaction + type: array + items: + $ref: '#/components/schemas/ErgoTransactionInput' + dataInputs: + description: Read-only inputs, that are not going to be spent by transaction. + type: array + items: + $ref: '#/components/schemas/ErgoTransactionDataInput' + outputs: + description: Outputs of the transaction, i.e. box candidates to be created by this transaction. + type: array + items: + $ref: '#/components/schemas/ErgoTransactionOutput' + size: + description: Size of ErgoTransaction in bytes + type: integer + format: int32 + + IndexedErgoTransaction: + type: object + description: Transaction indexed with extra information + required: + - id + - inputs + - dataInputs + - outputs + - inclusionHeight + - numConfirmations + - blockId + - timestamp + - index + - globalIndex + - size + properties: + id: + $ref: '#/components/schemas/TransactionId' + inputs: + description: Transaction inputs + type: array + items: + $ref: '#/components/schemas/ErgoTransactionInput' + dataInputs: + description: Transaction data inputs + type: array + items: + $ref: '#/components/schemas/ErgoTransactionDataInput' + outputs: + description: Transaction outputs + type: array + items: + $ref: '#/components/schemas/ErgoTransactionOutput' + inclusionHeight: + description: Height of a block the transaction was included in + type: integer + format: int32 + example: 20998 + numConfirmations: + description: Number of transaction confirmations + type: integer + format: int32 + example: 20998 + blockId: + description: Id of the block the transaction was included in + allOf: + - $ref: '#/components/schemas/ModifierId' + timestamp: + $ref: '#/components/schemas/Timestamp' + index: + description: index of the transaction in the block it was included in + type: integer + format: int32 + example: 3 + globalIndex: + description: Global index of the transaction in the blockchain + type: integer + format: int64 + example: 3565445 + size: + description: Size in bytes + type: integer + format: int32 + + ErgoAddress: + description: Encoded Ergo Address + type: string + example: '3WwbzW6u8hKWBcL1W7kNVMr25s2UHfSBnYtwSHvrRQt7DdPuoXrt' + + FullBlock: + description: Block with header and transactions + type: object + required: + - header + - blockTransactions + - adProofs + - extension + - size + properties: + header: + $ref: '#/components/schemas/BlockHeader' + blockTransactions: + $ref: '#/components/schemas/BlockTransactions' + adProofs: + $ref: '#/components/schemas/BlockADProofs' + extension: + $ref: '#/components/schemas/Extension' + size: + description: Size in bytes + type: integer + format: int32 + + PowSolutions: + description: An object containing all components of pow solution + type: object + required: + - pk + - w + - n + - d + properties: + pk: + type: string + description: Base16-encoded public key + example: '0350e25cee8562697d55275c96bb01b34228f9bd68fd9933f2a25ff195526864f5' + w: + type: string + example: '0366ea253123dfdb8d6d9ca2cb9ea98629e8f34015b1e4ba942b1d88badfcc6a12' + n: + type: string + example: '0000000000000000' + d: + type: number + example: 987654321 + + BlockHeader: + description: Header of a block. + It authenticates link to a previous block, other block sections + (transactions, UTXO set transformation proofs, extension), UTXO set, votes for blockchain parameters + to be changed and proof-of-work related data. + type: object + required: + - id + - timestamp + - version + - adProofsRoot + - stateRoot + - transactionsRoot + - nBits + - extensionHash + - powSolutions + - height + - difficulty + - parentId + - votes + properties: + id: + description: Block id + $ref: '#/components/schemas/ModifierId' + timestamp: + description: Block generation time reported by a miner + $ref: '#/components/schemas/Timestamp' + version: + description: Protocol version used to generate the block + $ref: '#/components/schemas/Version' + adProofsRoot: + description: Digest of UTXO set transformation proofs + $ref: '#/components/schemas/Digest32' + stateRoot: + description: AVL+ tree digest of UTXO set (after the block is applied) + $ref: '#/components/schemas/ADDigest' + transactionsRoot: + description: Merkle tree digest of transactions in the block (BlockTransactions section) + $ref: '#/components/schemas/Digest32' + nBits: + description: Proof-of-work target (difficulty encoded) + type: integer + format: int64 + minimum: 0 + example: 19857408 + extensionHash: + description: Merkle tree digest of the extension section of the block + $ref: '#/components/schemas/Digest32' + powSolutions: + description: Solution for the proof-of-work puzzle + $ref: '#/components/schemas/PowSolutions' + height: + description: Height of the block (genesis block height == 1) + type: integer + format: int32 + minimum: 0 + example: 667 + difficulty: + type: string + example: '9575989248' + parentId: + $ref: '#/components/schemas/ModifierId' + votes: + description: Votes for changing system parameters + $ref: '#/components/schemas/Votes' + size: + description: Size of the header in bytes + type: integer + format: int32 + extensionId: + description: Hash of the extension section of the block == hash(modifier type id, header id, extensionHash) + $ref: '#/components/schemas/ModifierId' + transactionsId: + description: Hash of the transactions section of the block == hash(modifier type id, header id, transactionsRoot) + $ref: '#/components/schemas/ModifierId' + adProofsId: + description: Hash of the UTXO set transformation proofs section of the block == hash(modifier type id, header id, adProofsRoot) + $ref: '#/components/schemas/ModifierId' + + BlockTransactions: + description: Section of a block which contains transactions. + type: object + required: + - headerId + - transactions + - size + properties: + headerId: + description: Identifier of a header of a corresponding block + $ref: '#/components/schemas/ModifierId' + transactions: + description: Transactions of the block + $ref: '#/components/schemas/Transactions' + size: + description: Size in bytes of all block transactions + type: integer + format: int32 + + BlockADProofs: + type: object + required: + - headerId + - proofBytes + - digest + - size + properties: + headerId: + description: Identifier of a header of the block which contains the proofs + $ref: '#/components/schemas/ModifierId' + proofBytes: + description: Serialized bytes of the authenticated dictionary proof + $ref: '#/components/schemas/SerializedAdProof' + digest: + description: Hash of the proofBytes + $ref: '#/components/schemas/Digest32' + size: + description: Size in bytes + type: integer + format: int32 + + Extension: + description: Section of a block which contains extension data. + type: object + required: + - headerId + - digest + - fields + properties: + headerId: + description: Identifier of a header of a corresponding block + $ref: '#/components/schemas/ModifierId' + digest: + description: Root hash (aka digest) merkelized list of key-value records + $ref: '#/components/schemas/Digest32' + fields: + description: List of key-value records + type: array + nullable: true + items: + $ref: '#/components/schemas/KeyValueItem' + + KeyValueItem: + description: Key-value record represented as a pair of hex strings in an array. + type: array + items: + $ref: '#/components/schemas/HexString' + + Peer: + type: object + required: + - address + properties: + address: + type: string + example: '127.0.0.1:5673' + restApiUrl: + type: string + example: 'https://example.com' + nullable: true + name: + type: string + example: mynode + nullable: true + lastSeen: + $ref: '#/components/schemas/Timestamp' + connectionType: + type: string + nullable: true + enum: + - Incoming + - Outgoing + + BlacklistedPeers: + type: object + required: + - addresses + properties: + addresses: + type: array + items: + type: string + description: Blacklisted node address + + NodeInfo: + description: Data container for /info API request output. + Contains information about node's state and configuration. + Contains data about best block, best header, state, etc. + Best block is the block with the maximum height. + type: object + required: + - name + - appVersion + - fullHeight + - headersHeight + - maxPeerHeight + - bestFullHeaderId + - previousFullHeaderId + - bestHeaderId + - headersScore + - fullBlocksScore + - stateRoot + - stateType + - stateVersion + - isMining + - peersCount + - unconfirmedCount + - difficulty + - currentTime + - launchTime + - genesisBlockId + - parameters + properties: + name: + description: Node's (peer) self-chosen name from config + type: string + example: my-node-1 + appVersion: + description: Node's application version + type: string + example: 0.0.1 + fullHeight: + type: integer + format: int32 + description: Height of the best block known to the node. + Can be 'null' if state is empty (no full block is applied since node launch) + minimum: 0 + example: 667 + nullable: true + headersHeight: + type: integer + format: int32 + description: The height of the best header (i.e. the one with the maximum height). + Can be 'null' if state is empty (no header applied since node launch) + minimum: 0 + example: 667 + nullable: true + maxPeerHeight: + type: integer + format: int32 + description: Maximum block height of connected peers. + Can be 'null' if state is empty (no peer connected since node launch) + minimum: 0 + example: 706162 + nullable: true + bestFullHeaderId: + type: string + description: Best full-block id (header id of such block). + Can be 'null' if no full block is applied since node launch. + nullable: true + allOf: + - $ref: '#/components/schemas/ModifierId' + previousFullHeaderId: + type: string + description: Header id of the parent block of the best full-block (i.e. previous block in the blockchain). + Can be 'null' if no full block is applied since node launch + nullable: true + allOf: + - $ref: '#/components/schemas/ModifierId' + bestHeaderId: + type: string + description: Best header ID (hex representation). + Can be 'null' if no header applied since node launch. + nullable: true + allOf: + - $ref: '#/components/schemas/ModifierId' + stateRoot: + type: string + nullable: true + description: Current UTXO set digest. + Can be 'null' if state is empty (no full block is applied since node launch) + example: 'dab9da11fc216660e974842cc3b7705e62ebb9e0bf5ff78e53f9cd40abadd117' + stateType: + description: Whether the node is storing UTXO set or only its digest. + Equals `digest` if only digest is stored, `utxo` if full UTXO set is stored. + type: string + enum: + - digest + - utxo + stateVersion: + description: Id of a block where UTXO set digest is taken from. + Can be 'null' if no full block is applied since node launch. + type: string + example: 'fab9da11fc216660e974842cc3b7705e62ebb9e0bf5ff78e53f9cd40abadd117' + nullable: true + isMining: + description: Whether the node is mining (i.e. generating blocks). + type: boolean + example: true + peersCount: + type: integer + description: Number of peers this node is connected with. + format: int32 + minimum: 0 + example: 327 + unconfirmedCount: + description: Number of unconfirmed transactions in the mempool. + type: integer + format: int32 + minimum: 0 + maximum: 10000 + example: 327 + difficulty: + type: integer + minimum: 0 + nullable: true + example: 667 + description: Difficulty on current bestFullHeaderId. + Can be 'null' if no full block is applied since node launch. + Difficulty is a BigInt integer. + currentTime: + type: integer + description: Current internal node time + allOf: + - $ref: '#/components/schemas/Timestamp' + launchTime: + type: integer + description: When the node was launched (in Java time format, UNIX time * 1000). + allOf: + - $ref: '#/components/schemas/Timestamp' + headersScore: + type: integer + description: Cumulative difficulty of best headers-chain. + Can be 'null' if no headers is applied since node launch. headersScore is a BigInt integer. + nullable: true + fullBlocksScore: + type: integer + description: Cumulative difficulty of best full blocks chain. + Can be 'null' if no full block is applied since node launch. fullBlocksScore is a BigInt integer. + nullable: true + genesisBlockId: + type: string + description: Header id of genesis block. Can be 'null' if genesis blocks is not produced yet. + nullable: true + allOf: + - $ref: '#/components/schemas/ModifierId' + parameters: + type: object + description: System parameters which could be readjusted via collective miners decision. + $ref: '#/components/schemas/Parameters' + eip27Supported: + type: boolean + example: true + description: Whether EIP-27 locked in + restApiUrl: + type: string + example: 'https://example.com' + description: Publicly accessible url of node which exposes restApi in firewall + + Parameters: + description: System parameters which could be readjusted via collective miners decision. + type: object + required: + - height + - blockVersion + - storageFeeFactor + - minValuePerByte + - maxBlockSize + - maxBlockCost + - tokenAccessCost + - inputCost + - dataInputCost + - outputCost + properties: + height: + type: integer + format: int32 + description: Height when current parameters were considered(not actual height). Can be '0' if state is empty + minimum: 0 + example: 667 + nullable: false + storageFeeFactor: + type: integer + format: int32 + description: Storage fee coefficient (per byte per storage period ~4 years) + minimum: 0 + example: 100000 + nullable: false + minValuePerByte: + type: integer + format: int32 + description: Minimum value per byte of an output + minimum: 0 + example: 360 + nullable: false + maxBlockSize: + type: integer + format: int32 + description: Maximum block size (in bytes) + minimum: 0 + example: 1048576 + nullable: false + maxBlockCost: + type: integer + format: int32 + description: Maximum cumulative computational cost of input scripts in block transactions + minimum: 0 + example: 104876 + nullable: false + blockVersion: + $ref: '#/components/schemas/Version' + nullable: false + tokenAccessCost: + type: integer + format: int32 + description: Validation cost of a single token + minimum: 0 + example: 100 + nullable: false + inputCost: + type: integer + format: int32 + description: Validation cost per one transaction input + minimum: 0 + example: 100 + nullable: false + dataInputCost: + type: integer + format: int32 + description: Validation cost per one data input + minimum: 0 + example: 100 + nullable: false + outputCost: + type: integer + format: int32 + description: Validation cost per one transaction output + minimum: 0 + example: 100 + nullable: false + + Version: + description: Ergo blockchain protocol version + type: integer + format: int8 + example: 2 + + TransactionBoxId: + description: Base16-encoded transaction box id bytes. Should be 32 bytes long + type: string + format: base16 + example: '1ab9da11fc216660e974842cc3b7705e62ebb9e0bf5ff78e53f9cd40abadd117' + + TransactionId: + description: Base16-encoded transaction id bytes + type: string + format: base16 + example: '2ab9da11fc216660e974842cc3b7705e62ebb9e0bf5ff78e53f9cd40abadd117' + + ErgoTree: + description: Base16-encoded ergo tree bytes + type: string + format: base16 + example: '0008cd0336100ef59ced80ba5f89c4178ebd57b6c1dd0f3d135ee1db9f62fc634d637041' + + Transactions: + description: List of ErgoTransaction objects + type: array + items: + $ref: '#/components/schemas/ErgoTransaction' + + FeeHistogramBin: + description: Fee histogram bin + type: object + properties: + nTxns: + type: integer + format: int32 + totalFee: + type: integer + format: int64 + + FeeHistogram: + description: Fee histogram for transactions in mempool + type: array + items: + $ref: '#/components/schemas/FeeHistogramBin' + + Asset: + description: Token detail in the transaction + type: object + required: + - tokenId + - amount + properties: + tokenId: + $ref: '#/components/schemas/Digest32' + amount: + description: Amount of the token + type: integer + format: int64 + example: 1000 + + Registers: + description: Ergo box registers + type: object + additionalProperties: + $ref: '#/components/schemas/SValue' + example: + R4: '100204a00b08cd0336100ef59ced80ba5f89c4178ebd57b6c1dd0f3d135ee1db9f62fc634d637041ea02d192a39a8cc7a70173007301' + + SValue: + description: Base-16 encoded serialized Sigma-state value + type: string + format: base16 + example: '100204a00b08cd0336100ef59ced80ba5f89c4178ebd57b6c1dd0f3d135ee1db9f62fc634d637041ea02d192a39a8cc7a70173007301' + + ModifierId: + description: Base16-encoded 32 byte modifier id + type: string + format: base16 + example: '3ab9da11fc216660e974842cc3b7705e62ebb9e0bf5ff78e53f9cd40abadd117' + + Digest32: + description: Base16-encoded 32 byte digest + type: string + format: base16 + example: '4ab9da11fc216660e974842cc3b7705e62ebb9e0bf5ff78e53f9cd40abadd117' + + HexString: + description: Base16-encoded bytes + type: string + format: base16 + example: '4ab9da11fc216660e974842cc3b7705e62ebb9e0bf5ff78e53f9cd40abadd117' + + ADDigest: + description: Base16-encoded 33 byte digest - digest with extra byte with tree height + type: string + format: base16 + example: '333ab9da11fc216660e974842cc3b7705e62ebb9e0bf5ff78e53f9cd40abadd117' + + SerializedAdProof: + description: Base16-encoded ad proofs + type: string + format: base16 + example: '3ab9da11fc216660e974842cc3b7705e62ebb9e0bf5ff78e53f9cd40abadd1173ab9da11fc216660e974842cc3b7705e62ebb9e0bf5ff78e53f9cd40abadd1173ab9da11fc216660e974842cc3b7705e62ebb9e0bf5ff78e53f9cd40abadd117' + + SpendingProofBytes: + description: Base16-encoded spending proofs + type: string + format: base16 + example: '4ab9da11fc216660e974842cc3b7705e62ebb9e0bf5ff78e53f9cd40abadd1173ab9da11fc216660e974842cc3b7705e62ebb9e0bf5ff78e53f9cd40abadd1173ab9da11fc216660e974842cc3b7705e62ebb9e0bf5ff78e53f9cd40abadd117' + + Timestamp: + description: Basic timestamp definition + type: integer + format: int64 + example: 1524143059077 + + AddressValidity: + description: Validity status of Ergo address + type: object + required: + - address + - isValid + properties: + address: + $ref: '#/components/schemas/ErgoAddress' + isValid: + type: boolean + error: + type: string + + ApiError: + description: Error response from API + type: object + required: + - error + - reason + - detail + properties: + error: + type: integer + description: Error code + example: 500 + reason: + type: string + description: Error message explaining the reason of the error + example: 'Internal server error' + detail: + type: string + nullable: true + description: Detailed error description + +paths: + /blocks/chainSlice: + get: + description: Get headers in a specified range of heights + operationId: getChainSlice + tags: + - blocks + parameters: + - in: query + name: fromHeight + required: false + description: Min header height (start of the range) + schema: + type: integer + format: int32 + default: 0 + - in: query + name: toHeight + required: false + description: Max header height of the range (last header height then omitted) + schema: + type: integer + format: int32 + default: -1 + responses: + '200': + description: Array of headers + content: + application/json: + schema: + type: array + description: Array of headers + items: + $ref: '#/components/schemas/BlockHeader' + default: + description: Error + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + + /info: + get: + description: Get the basic information about the status of Ergo Node. + operationId: getNodeInfo + tags: + - info + responses: + '200': + description: Node info object + content: + application/json: + schema: + $ref: '#/components/schemas/NodeInfo' + default: + description: Error + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + + /transactions/unconfirmed/byTransactionId/{txId}: + parameters: + - in: path + name: txId + required: true + description: ID of a transaction to return + schema: + type: string + get: + description: Get unconfirmed transaction from the mempool + operationId: getUnconfirmedTransactionById + tags: + - transactions + responses: + '200': + description: Ergo transaction + content: + application/json: + schema: + $ref: '#/components/schemas/ErgoTransaction' + default: + description: Error + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + + /transactions/poolHistogram: + parameters: + - in: query + name: bins + required: false + description: The number of bins in histogram + schema: + type: integer + format: int32 + minimum: 1 + default: 10 + - in: query + name: maxtime + required: false + description: Maximal wait time in milliseconds + schema: + type: integer + format: int64 + minimum: 0 + default: 60000 + get: + description: Get histogram (waittime, (n_trans, sum(fee)) for transactions in mempool. + It contains "bins"+1 bins, where i-th elements corresponds to transaction with wait time [i*maxtime/bins, (i+1)*maxtime/bins), + and last bin corresponds to the transactions with wait time >= maxtime. + operationId: getFeeHistogram + tags: + - transactions + responses: + '200': + description: Array with fee histogram + content: + application/json: + schema: + $ref: '#/components/schemas/FeeHistogram' + default: + description: Error + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + + /peers/connected: + get: + description: Get a list of current connected peers + operationId: getConnectedPeers + tags: + - peers + responses: + '200': + description: Array of peer objects + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Peer' + default: + description: Error + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + + /peers/blacklisted: + get: + description: Get a list of blacklisted peers + operationId: getBlacklistedPeers + tags: + - peers + responses: + '200': + description: Array of the addresses + content: + application/json: + schema: + $ref: '#/components/schemas/BlacklistedPeers' + + default: + description: Error + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + + /utils/address/{address}: + get: + description: Check address validity + operationId: CheckAddressValidityWithGet + tags: + - utils + parameters: + - in: path + name: address + required: true + description: address to check + schema: + $ref: '#/components/schemas/ErgoAddress' + responses: + '200': + description: Address validity with validation error + content: + application/json: + schema: + $ref: '#/components/schemas/AddressValidity' + default: + description: Error + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + + /blockchain/indexedHeight: + get: + description: Get current indexed block height. (The indexer has processed all blocks up to this height.) + operationId: getIndexedHeight + tags: + - blockchain + responses: + '200': + description: height of the indexer and full height + content: + application/json: + schema: + properties: + indexedHeight: + type: integer + default: 0 + description: number of blocks indexed + fullHeight: + type: integer + description: number of all known blocks + + /blockchain/transaction/byId/{txId}: + get: + description: Retrieve a transaction by its id + operationId: getTxById + tags: + - blockchain + parameters: + - in: path + name: txId + required: true + description: id of the wanted transaction + schema: + type: string + responses: + '200': + description: transaction with wanted id + content: + application/json: + schema: + $ref: '#/components/schemas/IndexedErgoTransaction' + '404': + description: Transaction with this id doesn't exist + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + default: + description: Error + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + + /blockchain/transaction/byAddress/{address}: + post: + description: Retrieve a list of transactions by their associated address + operationId: getTxsByAddress + tags: + - blockchain + parameters: + - in: path + name: address + required: true + description: address to retrieve associated transactions + schema: + $ref: '#/components/schemas/ErgoAddress' + - in: query + name: offset + required: false + description: amount of elements to skip from the start + schema: + type: integer + format: int32 + default: 0 + - in: query + name: limit + required: false + description: amount of elements to retrieve + schema: + type: integer + format: int32 + default: 5 + responses: + '200': + description: transactions associated with the given address + content: + application/json: + schema: + type: object + properties: + items: + type: array + description: Array of transactions + items: + $ref: '#/components/schemas/IndexedErgoTransaction' + total: + type: integer + description: Total count of retrieved transactions + '404': + description: No transactions found for wanted address + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + default: + description: Error + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + + /blockchain/box/byId/{boxId}: + get: + description: Retrieve a box by its id + operationId: getBoxById + tags: + - blockchain + parameters: + - in: path + name: boxId + required: true + description: id of the wanted box + schema: + type: string + responses: + '200': + description: box with wanted id + content: + application/json: + schema: + $ref: '#/components/schemas/IndexedErgoBox' + '404': + description: No box found with wanted id + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + default: + description: Error + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + + /blockchain/box/byAddress/{address}: + post: + description: Retrieve boxes by their associated address + operationId: getBoxesByAddress + tags: + - blockchain + parameters: + - in: path + name: address + required: true + description: address to retrieve boxes for + schema: + $ref: '#/components/schemas/ErgoAddress' + - in: query + name: offset + required: false + description: amount of elements to skip from the start + schema: + type: integer + format: int32 + default: 0 + - in: query + name: limit + required: false + description: amount of elements to retrieve + schema: + type: integer + format: int32 + default: 5 + responses: + '200': + description: boxes associated with wanted address + content: + application/json: + schema: + type: object + properties: + items: + type: array + description: Array of boxes + items: + $ref: '#/components/schemas/IndexedErgoBox' + total: + type: integer + description: Total number of retreived boxes + '404': + description: No boxes found for wanted address + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + default: + description: Error + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + + /blockchain/box/unspent/byAddress/{address}: + get: + description: Retrieve unspent boxes by their associated address + operationId: getBoxesByAddressUnspent + tags: + - blockchain + parameters: + - in: path + name: address + required: true + description: address to retrieve boxes for + schema: + $ref: '#/components/schemas/ErgoAddress' + - in: query + name: offset + required: false + description: amount of elements to skip from the start + schema: + type: integer + format: int32 + default: 0 + - in: query + name: limit + required: false + description: amount of elements to retrieve + schema: + type: integer + format: int32 + default: 5 + - in: query + name: sortDirection + required: false + description: desc = new boxes first ; asc = old boxes first + schema: + type: string + default: desc + responses: + '200': + description: unspent boxes associated with wanted address + content: + application/json: + schema: + type: array + description: Array of boxes + items: + $ref: '#/components/schemas/IndexedErgoBox' + '404': + description: No unspent boxes found for wanted address + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + default: + description: Error + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + + /blockchain/token/byId/{tokenId}: + get: + description: Retrieve minting information about a token + operationId: getTokenById + tags: + - blockchain + parameters: + - in: path + name: tokenId + required: true + description: id of the wanted token + schema: + type: string + responses: + '200': + description: token with wanted id + content: + application/json: + schema: + $ref: '#/components/schemas/IndexedToken' + '404': + description: No token found with wanted id + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + default: + description: Error + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + + /blockchain/balanceForAddress/{address}: + get: + description: Retrieve balance information of an Ergo address. + Separately return confirmed and unconfirmed balance information. + operationId: getBalanceForAddress + tags: + - blockchain + parameters: + - in: path + name: address + required: true + description: address to retrieve balance information for + schema: + $ref: '#/components/schemas/ErgoAddress' + responses: + '200': + description: balance information + content: + application/json: + schema: + type: object + properties: + confirmed: + description: confirmed balance of the address + $ref: '#/components/schemas/BalanceInfo' + unconfirmed: + description: unconfirmed balance of the address + $ref: '#/components/schemas/BalanceInfo' + default: + description: Error + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' \ No newline at end of file diff --git a/src/main/resources/api/openapi.yaml b/src/main/resources/api/openapi.yaml index af4b22671b..9587de29f5 100644 --- a/src/main/resources/api/openapi.yaml +++ b/src/main/resources/api/openapi.yaml @@ -243,71 +243,53 @@ components: description: Name of the token, if any IndexedErgoBox: - type: object - description: Box indexed with extra information - required: - - box - - confirmationsNum - - address - - creationTransaction - - spendingTransaction - - spendingHeight - - inclusionHeight - - spent - - globalIndex - properties: - box: - $ref: '#/components/schemas/ErgoTransactionOutput' - confirmationsNum: - description: Number of confirmations, if the box is included into the blockchain - type: integer - format: int32 - minimum: 0 - example: 147 - nullable: true - address: - $ref: '#/components/schemas/ErgoAddress' - creationTransaction: - description: Transaction which created the box - $ref: '#/components/schemas/ModifierId' - spendingTransaction: - description: Transaction which created the box - nullable: true - $ref: '#/components/schemas/ModifierId' - spendingHeight: - description: The height the box was spent at - type: integer - format: int32 - minimum: 0 - example: 147 - nullable: true - inclusionHeight: - description: The height the transaction containing the box was included in a block at - type: integer - format: int32 - minimum: 0 - example: 147 - spent: - description: A flag signalling whether the box was spent - type: boolean - example: false - globalIndex: - description: Global index of the output in the blockchain - type: integer - format: int64 - minimum: 0 - example: 83927 + allOf: + - $ref: '#/components/schemas/ErgoTransactionOutput' + - type: object + description: Box indexed with extra information + required: + - address + - spentTransactionId + - spendingHeight + - inclusionHeight + - globalIndex + properties: + address: + $ref: '#/components/schemas/ErgoAddress' + spentTransactionId: + description: Transaction which spent the box + nullable: true + $ref: '#/components/schemas/ModifierId' + spendingHeight: + description: The height the box was spent at + type: integer + format: int32 + minimum: 0 + example: 147 + nullable: true + inclusionHeight: + description: The height the transaction containing the box was included in a block at + type: integer + format: int32 + minimum: 0 + example: 147 + globalIndex: + description: Global index of the output in the blockchain + type: integer + format: int64 + minimum: 0 + example: 83927 IndexedToken: type: object description: Token indexed with extra information required: - -id - -boxId - -emissionAmount - -name - -description - -decimals + - id + - boxId + - emissionAmount + - name + - description + - decimals properties: id: description: Id of the token @@ -362,31 +344,32 @@ components: ErgoTransaction: type: object - description: Ergo transaction + description: ErgoTransaction is an atomic operation which changes UTXO state. required: - inputs - dataInputs - outputs properties: id: + description: Id of the transaction $ref: '#/components/schemas/TransactionId' inputs: - description: Inputs of the transaction + description: Inputs, that will be spent by this transaction type: array items: $ref: '#/components/schemas/ErgoTransactionInput' dataInputs: - description: Data inputs of the transaction + description: Read-only inputs, that are not going to be spent by transaction. type: array items: $ref: '#/components/schemas/ErgoTransactionDataInput' outputs: - description: Outputs of the transaction + description: Outputs of the transaction, i.e. box candidates to be created by this transaction. type: array items: $ref: '#/components/schemas/ErgoTransactionOutput' size: - description: Size in bytes + description: Size of ErgoTransaction in bytes type: integer format: int32 @@ -1540,6 +1523,10 @@ components: $ref: '#/components/schemas/BlockHeader' BlockHeader: + description: Header of a block. + It authenticates link to a previous block, other block sections + (transactions, UTXO set transformation proofs, extension), UTXO set, votes for blockchain parameters + to be changed and proof-of-work related data. type: object required: - id @@ -1557,27 +1544,37 @@ components: - votes properties: id: + description: Block id $ref: '#/components/schemas/ModifierId' timestamp: + description: Block generation time reported by a miner $ref: '#/components/schemas/Timestamp' version: + description: Protocol version used to generate the block $ref: '#/components/schemas/Version' adProofsRoot: + description: Digest of UTXO set transformation proofs $ref: '#/components/schemas/Digest32' stateRoot: + description: AVL+ tree digest of UTXO set (after the block is applied) $ref: '#/components/schemas/ADDigest' transactionsRoot: + description: Merkle tree digest of transactions in the block (BlockTransactions section) $ref: '#/components/schemas/Digest32' nBits: + description: Proof-of-work target (difficulty encoded) type: integer format: int64 minimum: 0 example: 19857408 extensionHash: + description: Merkle tree digest of the extension section of the block $ref: '#/components/schemas/Digest32' powSolutions: + description: Solution for the proof-of-work puzzle $ref: '#/components/schemas/PowSolutions' height: + description: Height of the block (genesis block height == 1) type: integer format: int32 minimum: 0 @@ -1588,19 +1585,24 @@ components: parentId: $ref: '#/components/schemas/ModifierId' votes: + description: Votes for changing system parameters $ref: '#/components/schemas/Votes' size: - description: Size in bytes + description: Size of the header in bytes type: integer format: int32 extensionId: + description: Hash of the extension section of the block == hash(modifier type id, header id, extensionHash) $ref: '#/components/schemas/ModifierId' transactionsId: + description: Hash of the transactions section of the block == hash(modifier type id, header id, transactionsRoot) $ref: '#/components/schemas/ModifierId' adProofsId: + description: Hash of the UTXO set transformation proofs section of the block == hash(modifier type id, header id, adProofsRoot) $ref: '#/components/schemas/ModifierId' BlockTransactions: + description: Section of a block which contains transactions. type: object required: - headerId @@ -1608,11 +1610,13 @@ components: - size properties: headerId: + description: Identifier of a header of a corresponding block $ref: '#/components/schemas/ModifierId' transactions: + description: Transactions of the block $ref: '#/components/schemas/Transactions' size: - description: Size in bytes + description: Size in bytes of all block transactions type: integer format: int32 @@ -1625,10 +1629,13 @@ components: - size properties: headerId: + description: Identifier of a header of the block which contains the proofs $ref: '#/components/schemas/ModifierId' proofBytes: + description: Serialized bytes of the authenticated dictionary proof $ref: '#/components/schemas/SerializedAdProof' digest: + description: Hash of the proofBytes $ref: '#/components/schemas/Digest32' size: description: Size in bytes @@ -1636,6 +1643,7 @@ components: format: int32 Extension: + description: Section of a block which contains extension data. type: object required: - headerId @@ -1643,8 +1651,10 @@ components: - fields properties: headerId: + description: Identifier of a header of a corresponding block $ref: '#/components/schemas/ModifierId' digest: + description: Root hash (aka digest) merkelized list of key-value records $ref: '#/components/schemas/Digest32' fields: description: List of key-value records @@ -1654,6 +1664,7 @@ components: $ref: '#/components/schemas/KeyValueItem' KeyValueItem: + description: Key-value record represented as a pair of hex strings in an array. type: array items: $ref: '#/components/schemas/HexString' @@ -2047,6 +2058,10 @@ components: description: Blacklisted node address NodeInfo: + description: Data container for /info API request output. + Contains information about node's state and configuration. + Contains data about best block, best header, state, etc. + Best block is the block with the maximum height. type: object required: - name @@ -2072,76 +2087,89 @@ components: - parameters properties: name: + description: Node's (peer) self-chosen name from config type: string example: my-node-1 appVersion: + description: Node's application version type: string example: 0.0.1 fullHeight: type: integer format: int32 - description: Can be 'null' if state is empty (no full block is applied since node launch) + description: Height of the best block known to the node. + Can be 'null' if state is empty (no full block is applied since node launch) minimum: 0 example: 667 nullable: true headersHeight: type: integer format: int32 - description: Can be 'null' if state is empty (no header applied since node launch) + description: The height of the best header (i.e. the one with the maximum height). + Can be 'null' if state is empty (no header applied since node launch) minimum: 0 example: 667 nullable: true maxPeerHeight: type: integer format: int32 - description: Maximum block height of connected peers. Can be 'null' if state is empty (no peer connected since node launch) + description: Maximum block height of connected peers. + Can be 'null' if state is empty (no peer connected since node launch) minimum: 0 example: 706162 nullable: true bestFullHeaderId: type: string - description: Can be 'null' if no full block is applied since node launch + description: Best full-block id (header id of such block). + Can be 'null' if no full block is applied since node launch. nullable: true allOf: - $ref: '#/components/schemas/ModifierId' previousFullHeaderId: type: string - description: Can be 'null' if no full block is applied since node launch + description: Header id of the parent block of the best full-block (i.e. previous block in the blockchain). + Can be 'null' if no full block is applied since node launch nullable: true allOf: - $ref: '#/components/schemas/ModifierId' bestHeaderId: type: string - description: Can be 'null' if no header applied since node launch + description: Best header ID (hex representation). + Can be 'null' if no header applied since node launch. nullable: true allOf: - $ref: '#/components/schemas/ModifierId' stateRoot: type: string nullable: true - description: Can be 'null' if state is empty (no full block is applied since node launch) + description: Current UTXO set digest. + Can be 'null' if state is empty (no full block is applied since node launch) example: 'dab9da11fc216660e974842cc3b7705e62ebb9e0bf5ff78e53f9cd40abadd117' stateType: + description: Whether the node is storing UTXO set or only its digest. + Equals `digest` if only digest is stored, `utxo` if full UTXO set is stored. type: string enum: - digest - utxo stateVersion: - description: Can be 'null' if no full block is applied since node launch + description: Id of a block where UTXO set digest is taken from. + Can be 'null' if no full block is applied since node launch. type: string example: 'fab9da11fc216660e974842cc3b7705e62ebb9e0bf5ff78e53f9cd40abadd117' nullable: true isMining: + description: Whether the node is mining (i.e. generating blocks). type: boolean example: true peersCount: type: integer - description: Number of connected peers + description: Number of peers this node is connected with. format: int32 minimum: 0 example: 327 unconfirmedCount: - description: Current unconfirmed transactions count + description: Number of unconfirmed transactions in the mempool. type: integer format: int32 minimum: 0 @@ -2152,8 +2180,8 @@ components: minimum: 0 nullable: true example: 667 - description: > - Difficulty on current bestFullHeaderId. Can be 'null' if no full block is applied since node launch. + description: Difficulty on current bestFullHeaderId. + Can be 'null' if no full block is applied since node launch. Difficulty is a BigInt integer. currentTime: type: integer @@ -2162,26 +2190,28 @@ components: - $ref: '#/components/schemas/Timestamp' launchTime: type: integer - description: Time when the node was started + description: When the node was launched (in Java time format, UNIX time * 1000). allOf: - $ref: '#/components/schemas/Timestamp' headersScore: type: integer - description: Can be 'null' if no headers is applied since node launch. headersScore is a BigInt integer. + description: Cumulative difficulty of best headers-chain. + Can be 'null' if no headers is applied since node launch. headersScore is a BigInt integer. nullable: true fullBlocksScore: type: integer - description: Can be 'null' if no full block is applied since node launch. fullBlocksScore is a BigInt integer. + description: Cumulative difficulty of best full blocks chain. + Can be 'null' if no full block is applied since node launch. fullBlocksScore is a BigInt integer. nullable: true genesisBlockId: type: string - description: Can be 'null' if genesis blocks is not produced yet + description: Header id of genesis block. Can be 'null' if genesis blocks is not produced yet. nullable: true allOf: - $ref: '#/components/schemas/ModifierId' parameters: type: object - description: current parameters + description: System parameters which could be readjusted via collective miners decision. $ref: '#/components/schemas/Parameters' eip27Supported: type: boolean @@ -2193,6 +2223,7 @@ components: description: Publicly accessible url of node which exposes restApi in firewall Parameters: + description: System parameters which could be readjusted via collective miners decision. type: object required: - height @@ -2306,7 +2337,7 @@ components: example: '02a7955281885bf0f0ca4a48678848cad8dc5b328ce8bc1d4481d041c98e891ff3' Transactions: - description: Ergo transaction objects + description: List of ErgoTransaction objects type: array items: $ref: '#/components/schemas/ErgoTransaction' @@ -2472,6 +2503,7 @@ components: type: string ApiError: + description: Error response from API type: object required: - error @@ -2484,7 +2516,7 @@ components: example: 500 reason: type: string - description: String error code + description: Error message explaining the reason of the error example: 'Internal server error' detail: type: string @@ -2494,7 +2526,8 @@ components: paths: /blocks: get: - summary: Get the Array of header ids + summary: Get an array of header ids (hex encoded) for the given range of blockchain block heights. + Returns a page of the whole list starting from `offset` and containing `limit` items. operationId: getHeaderIds tags: - blocks @@ -2512,7 +2545,7 @@ paths: - in: query name: offset required: false - description: The number of items in list to skip + description: The first block height to include in the list schema: type: integer format: int32 @@ -2559,7 +2592,7 @@ paths: /blocks/at/{blockHeight}: get: - summary: Get the header ids at a given height + summary: Get header ids at the given height operationId: getFullBlockAt tags: - blocks @@ -2567,7 +2600,7 @@ paths: - in: path name: blockHeight required: true - description: Height of a wanted block + description: Height of a block to retrieve header ids schema: type: integer format: int32 @@ -2598,7 +2631,7 @@ paths: /blocks/chainSlice: get: - summary: Get headers in a specified range + summary: Get headers in a specified range of heights operationId: getChainSlice tags: - blocks @@ -2606,7 +2639,7 @@ paths: - in: query name: fromHeight required: false - description: Min header height + description: Min header height (start of the range) schema: type: integer format: int32 @@ -2614,7 +2647,7 @@ paths: - in: query name: toHeight required: false - description: Max header height (best header height by default) + description: Max header height of the range (last header height then omitted) schema: type: integer format: int32 @@ -2638,7 +2671,7 @@ paths: /blocks/{headerId}: get: - summary: Get the full block info by a given signature + summary: Get the full block info by a given header id operationId: getFullBlockById tags: - blocks @@ -2647,12 +2680,12 @@ paths: - in: path name: headerId required: true - description: ID of a wanted block + description: ID of the header the wanted block schema: type: string responses: '200': - description: Block object + description: Block object representing the full block data content: application/json: schema: @@ -2672,7 +2705,7 @@ paths: /blocks/{headerId}/header: get: - summary: Get the block header info by a given signature + summary: Get the block header info by a given header id operationId: getBlockHeaderById tags: - blocks @@ -2782,7 +2815,7 @@ paths: - in: path name: count required: true - description: count of a wanted block headers + description: a number of block headers to return schema: type: number responses: @@ -5752,7 +5785,7 @@ paths: /blockchain/indexedHeight: get: - summary: Get current block height the indexer is at + summary: Get current indexed block height. (The indexer has processed all blocks up to this height.) operationId: getIndexedHeight tags: - blockchain @@ -6101,6 +6134,13 @@ paths: schema: type: string default: desc + - in: query + name: includeUnconfirmed + required: false + description: if true include unconfirmed transactions from mempool + schema: + type: boolean + default: false responses: '200': description: unspent boxes associated with wanted address @@ -6256,6 +6296,13 @@ paths: schema: type: string default: desc + - in: query + name: includeUnconfirmed + required: false + description: if true include unconfirmed transactions from mempool + schema: + type: boolean + default: false responses: '200': description: unspent boxes with wanted ergotree diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 5cde542d65..b408c2461e 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -42,9 +42,6 @@ ergo { # how many different proofs we are downloading from other peers # and comparing with each other, before choosing best one p2pNipopows = 1 - - # Minimal suffix size for NiPoPoW proof (may be pre-defined constant or settings parameter) - nipopowSuffix = 10 } # Is the node is doing mining diff --git a/src/main/scala/org/ergoplatform/http/ErgoHttpService.scala b/src/main/scala/org/ergoplatform/http/ErgoHttpService.scala index 1bc963cbe6..c331e005e2 100644 --- a/src/main/scala/org/ergoplatform/http/ErgoHttpService.scala +++ b/src/main/scala/org/ergoplatform/http/ErgoHttpService.scala @@ -2,10 +2,14 @@ package org.ergoplatform.http import akka.actor.ActorSystem import akka.http.scaladsl.model.StatusCodes +import akka.http.scaladsl.model.HttpHeader import akka.http.scaladsl.server.{ExceptionHandler, RejectionHandler, Route} +import akka.http.scaladsl.server.Directive0 import akka.http.scaladsl.server.directives.RouteDirectives import scorex.core.api.http.{ApiErrorHandler, ApiRejectionHandler, ApiRoute, CorsHandler} +import akka.http.scaladsl.model.headers._ +import scala.collection.immutable final case class ErgoHttpService( apiRoutes: Seq[ApiRoute], @@ -17,6 +21,21 @@ final case class ErgoHttpService( def exceptionHandler: ExceptionHandler = ApiErrorHandler.exceptionHandler + private val corsResponseHeaders: List[ModeledHeader] = List[ModeledHeader]( + `Access-Control-Allow-Origin`.*, + `Access-Control-Allow-Credentials`(true), + `Access-Control-Allow-Headers`("Authorization", "Content-Type", "X-Requested-With", "api_key", + "openai-conversation-id", + "openai-ephemeral-user-id", + "baggage", + "sentry-trace" + ) + ) + + override def respondWithHeaders(responseHeaders: immutable.Seq[HttpHeader]): Directive0 = { + super.respondWithHeaders(corsResponseHeaders) + } + val compositeRoute: Route = handleRejections(rejectionHandler) { handleExceptions(exceptionHandler) { diff --git a/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala b/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala index 013a747a76..424c279c99 100644 --- a/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala +++ b/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala @@ -27,15 +27,18 @@ import scorex.crypto.hash.Digest import scorex.util.encode.Base16 import sigmastate.Values.SigmaBoolean import sigmastate._ -import sigmastate.basics.CryptoConstants.EcPointType -import sigmastate.basics.DLogProtocol.{DLogProverInput, FirstDLogProverMessage, ProveDlog} -import sigmastate.basics.VerifierMessage.Challenge -import sigmastate.basics._ +import sigmastate.crypto.CryptoConstants.EcPointType +import sigmastate.crypto.DLogProtocol.{DLogProverInput, FirstDLogProverMessage, ProveDlog} +import sigmastate.crypto.VerifierMessage.Challenge +import sigmastate.crypto._ import sigmastate.interpreter._ import sigmastate.serialization.OpCodes -import special.sigma.AnyValue +import sigma.AnyValue import org.ergoplatform.nodeView.state.SnapshotsInfo import org.ergoplatform.nodeView.state.UtxoState.ManifestId +import org.ergoplatform.sdk.JsonCodecs +import sigmastate.eval.Extensions.ArrayOps + import java.math.BigInteger import scala.util.{Failure, Success, Try} @@ -198,7 +201,14 @@ trait ApiCodecs extends JsonCodecs { } yield ErgoTransaction(ergoLikeTx) } - + implicit val sigmaLeafEncoder: Encoder[SigmaLeaf] = { + leaf => + val op = leaf.opCode.toByte.asJson + leaf match { + case dlog: ProveDlog => Map("op" -> op, "h" -> dlog.value.asJson).asJson + case dht: ProveDHTuple => Map("op" -> op, "g" -> dht.g.asJson, "h" -> dht.h.asJson, "u" -> dht.u.asJson, "v" -> dht.v.asJson).asJson + } + } implicit val sigmaBooleanEncoder: Encoder[SigmaBoolean] = { sigma => @@ -216,6 +226,16 @@ trait ApiCodecs extends JsonCodecs { } } + implicit val sigmaLeafDecoder: Decoder[SigmaLeaf] = Decoder.instance { c => + c.downField("op").as[Byte].flatMap { + case b: Byte if b == OpCodes.ProveDlogCode => + c.downField("h").as[EcPointType].map(h => ProveDlog(h)) + case _ => + //only dlog is supported for now + Left(DecodingFailure("Unsupported value", List())) + } + } + implicit val sigmaBooleanDecoder: Decoder[SigmaBoolean] = Decoder.instance { c => c.downField("op").as[Byte].flatMap { case b: Byte if b == OpCodes.ProveDlogCode => @@ -249,7 +269,7 @@ trait ApiCodecs extends JsonCodecs { implicit val firstProverMessageEncoder: Encoder[FirstProverMessage] = { case cmtDlog: FirstDLogProverMessage => Json.obj("type" -> "dlog".asJson, "a" -> cmtDlog.ecData.asJson) - case cmtDht: FirstDiffieHellmanTupleProverMessage => + case cmtDht: FirstDHTupleProverMessage => Json.obj("type" -> "dht".asJson, "a" -> cmtDht.a.asJson, "b" -> cmtDht.b.asJson) case _ => ??? } @@ -264,7 +284,7 @@ trait ApiCodecs extends JsonCodecs { for { a <- c.downField("a").as[EcPointType] b <- c.downField("b").as[EcPointType] - } yield FirstDiffieHellmanTupleProverMessage(a, b) + } yield FirstDHTupleProverMessage(a, b) case _ => Left(DecodingFailure("Unsupported sigma-protocol type value", List())) } @@ -302,20 +322,20 @@ trait ApiCodecs extends JsonCodecs { case h: String if h == "cmtWithSecret" => for { secret <- c.downField("secret").as[BigInteger] - pubkey <- c.downField("pubkey").as[SigmaBoolean] + pubkey <- c.downField("pubkey").as[SigmaLeaf] position <- c.downField("position").as[NodePosition] firstMsg <- firstProverMessageDecoder.tryDecode(c) } yield OwnCommitment(pubkey, secret, firstMsg, position) case h: String if h == "cmtReal" => for { - pubkey <- c.downField("pubkey").as[SigmaBoolean] + pubkey <- c.downField("pubkey").as[SigmaLeaf] position <- c.downField("position").as[NodePosition] firstMsg <- firstProverMessageDecoder.tryDecode(c) } yield RealCommitment(pubkey, firstMsg, position) case h: String if h == "cmtSimulated" => for { position <- c.downField("position").as[NodePosition] - pubkey <- c.downField("pubkey").as[SigmaBoolean] + pubkey <- c.downField("pubkey").as[SigmaLeaf] firstMsg <- firstProverMessageDecoder.tryDecode(c) } yield SimulatedCommitment(pubkey, firstMsg, position) case _ => @@ -332,7 +352,7 @@ trait ApiCodecs extends JsonCodecs { Json.obj( "hint" -> proofType.asJson, - "challenge" -> Base16.encode(sp.challenge).asJson, + "challenge" -> Base16.encode(sp.challenge.toArray).asJson, "pubkey" -> sp.image.asJson, "proof" -> SigSerializer.toProofBytes(sp.uncheckedTree).asJson, "position" -> sp.position.asJson @@ -344,26 +364,26 @@ trait ApiCodecs extends JsonCodecs { case h: String if h == "proofReal" => for { challenge <- c.downField("challenge").as[String] - pubkey <- c.downField("pubkey").as[SigmaBoolean] + pubkey <- c.downField("pubkey").as[SigmaLeaf] proof <- c.downField("proof").as[String] position <- c.downField("position").as[NodePosition] } yield RealSecretProof( pubkey, - Challenge @@ Base16.decode(challenge).get, + Challenge @@ Base16.decode(challenge).get.toColl, SigSerializer.parseAndComputeChallenges(pubkey, Base16.decode(proof).get)(null), position ) case h: String if h == "proofSimulated" => for { challenge <- c.downField("challenge").as[String] - pubkey <- c.downField("pubkey").as[SigmaBoolean] + pubkey <- c.downField("pubkey").as[SigmaLeaf] proof <- c.downField("proof").as[String] position <- c.downField("position").as[NodePosition] } yield SimulatedSecretProof( pubkey, - Challenge @@ Base16.decode(challenge).get, + Challenge @@ Base16.decode(challenge).get.toColl, SigSerializer.parseAndComputeChallenges(pubkey, Base16.decode(proof).get)(null), position ) diff --git a/src/main/scala/org/ergoplatform/http/api/BlockchainApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/BlockchainApiRoute.scala index cdc4d7feca..df2bfbbb71 100644 --- a/src/main/scala/org/ergoplatform/http/api/BlockchainApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/BlockchainApiRoute.scala @@ -46,6 +46,8 @@ case class BlockchainApiRoute(readersHolder: ActorRef, ergoSettings: ErgoSetting } private val sortDir: Directive[Tuple1[Direction]] = parameters("sortDirection".as(sortMarshaller) ? DESC) + private val unconfirmed: Directive[Tuple1[Boolean]] = parameters("includeUnconfirmed".as[Boolean] ? false) + /** * Total number of boxes/transactions that can be requested at once to avoid too heavy requests ([[BlocksApiRoute.MaxHeaders]]) */ @@ -68,16 +70,20 @@ case class BlockchainApiRoute(readersHolder: ActorRef, ergoSettings: ErgoSetting getTxByIdR ~ getTxByIndexR ~ getTxsByAddressR ~ + getTxsByAddressGetRoute ~ getTxRangeR ~ getBoxByIdR ~ getBoxByIndexR ~ getBoxesByAddressR ~ + getBoxesByAddressGetRoute ~ getBoxesByAddressUnspentR ~ + getBoxesByAddressUnspentGetRoute ~ getBoxRangeR ~ getBoxesByErgoTreeR ~ getBoxesByErgoTreeUnspentR ~ getTokenInfoByIdR ~ - getAddressBalanceTotalR + getAddressBalanceTotalR ~ + getAddressBalanceTotalGetRoute } else pathPrefix("blockchain") { @@ -90,11 +96,11 @@ case class BlockchainApiRoute(readersHolder: ActorRef, ergoSettings: ErgoSetting private def getHistoryWithMempool: Future[(ErgoHistoryReader,ErgoMemPoolReader)] = (readersHolder ? GetReaders).mapTo[Readers].map(r => (r.h, r.m)) - private def getAddress(tree: ErgoTree)(history: ErgoHistoryReader): Option[IndexedErgoAddress] = { + private def getAddress(tree: ErgoTree)(history: ErgoHistoryReader): Option[IndexedErgoAddress] = history.typedExtraIndexById[IndexedErgoAddress](hashErgoTree(tree)) - } - private def getAddress(addr: ErgoAddress)(history: ErgoHistoryReader): Option[IndexedErgoAddress] = getAddress(addr.script)(history) + private def getAddress(addr: ErgoAddress)(history: ErgoHistoryReader): Option[IndexedErgoAddress] = + getAddress(addr.script)(history) private def getTxById(id: ModifierId)(history: ErgoHistoryReader): Option[IndexedErgoTransaction] = history.typedExtraIndexById[IndexedErgoTransaction](id) match { @@ -147,14 +153,24 @@ case class BlockchainApiRoute(readersHolder: ActorRef, ergoSettings: ErgoSetting } } - private def getTxsByAddressR: Route = (post & pathPrefix("transaction" / "byAddress") & ergoAddress & paging) { (address, offset, limit) => - if(limit > MaxItems) { + private def validateAndGetTxsByAddress(address: ErgoAddress, + offset: Int, + limit: Int): Route = { + if (limit > MaxItems) { BadRequest(s"No more than $MaxItems transactions can be requested") - }else { + } else { ApiResponse(getTxsByAddress(address, offset, limit)) } } + private def getTxsByAddressR: Route = (post & pathPrefix("transaction" / "byAddress") & ergoAddress & paging) { (address, offset, limit) => + validateAndGetTxsByAddress(address, offset, limit) + } + + private def getTxsByAddressGetRoute: Route = (pathPrefix("transaction" / "byAddress") & get & addressPass & paging) { (address, offset, limit) => + validateAndGetTxsByAddress(address, offset, limit) + } + private def getTxRange(offset: Int, limit: Int): Future[Seq[ModifierId]] = getHistory.map { history => val base: Long = getIndex(GlobalTxIndexKey, history).getLong - offset @@ -205,32 +221,58 @@ case class BlockchainApiRoute(readersHolder: ActorRef, ergoSettings: ErgoSetting } } - private def getBoxesByAddressR: Route = (post & pathPrefix("box" / "byAddress") & ergoAddress & paging) { (address, offset, limit) => - if(limit > MaxItems) { + private def validateAndGetBoxesByAddress(address: ErgoAddress, + offset: Int, + limit: Int) = { + if (limit > MaxItems) { BadRequest(s"No more than $MaxItems boxes can be requested") - }else { + } else { ApiResponse(getBoxesByAddress(address, offset, limit)) } } - private def getBoxesByAddressUnspent(addr: ErgoAddress, offset: Int, limit: Int, sortDir: Direction): Future[Seq[IndexedErgoBox]] = - getHistory.map { history => + private def getBoxesByAddressR: Route = (post & pathPrefix("box" / "byAddress") & ergoAddress & paging) { (address, offset, limit) => + validateAndGetBoxesByAddress(address, offset, limit) + } + + private def getBoxesByAddressGetRoute: Route = (pathPrefix("box" / "byAddress") & get & addressPass & paging) { (address, offset, limit) => + validateAndGetBoxesByAddress(address, offset, limit) + } + + private def getBoxesByAddressUnspent(addr: ErgoAddress, offset: Int, limit: Int, sortDir: Direction, unconfirmed: Boolean): Future[Seq[IndexedErgoBox]] = + getHistoryWithMempool.map { case (history, mempool) => getAddress(addr)(history) match { - case Some(addr) => addr.retrieveUtxos(history, offset, limit, sortDir) + case Some(addr) => addr.retrieveUtxos(history, mempool, offset, limit, sortDir, unconfirmed) case None => Seq.empty[IndexedErgoBox] } } - private def getBoxesByAddressUnspentR: Route = (post & pathPrefix("box" / "unspent" / "byAddress") & ergoAddress & paging & sortDir) { (address, offset, limit, dir) => - if(limit > MaxItems) { + private def validateAndGetBoxesByAddressUnspent(address: ErgoAddress, + offset: Int, + limit: Int, + dir: Direction, + unconfirmed: Boolean): Route = { + if (limit > MaxItems) { BadRequest(s"No more than $MaxItems boxes can be requested") - }else if(dir == SortDirection.INVALID) { + } else if (dir == SortDirection.INVALID) { BadRequest("Invalid parameter for sort direction, valid values are \"ASC\" and \"DESC\"") - }else { - ApiResponse(getBoxesByAddressUnspent(address, offset, limit, dir)) + } else { + ApiResponse(getBoxesByAddressUnspent(address, offset, limit, dir, unconfirmed)) } } + private def getBoxesByAddressUnspentR: Route = + (post & pathPrefix("box" / "unspent" / "byAddress") & ergoAddress & paging & sortDir & unconfirmed) { + (address, offset, limit, dir, unconfirmed) => + validateAndGetBoxesByAddressUnspent(address, offset, limit, dir, unconfirmed) + } + + private def getBoxesByAddressUnspentGetRoute: Route = + (pathPrefix("box" / "unspent" / "byAddress") & get & addressPass & paging & sortDir & unconfirmed) { + (address, offset, limit, dir, unconfirmed) => + validateAndGetBoxesByAddressUnspent(address, offset, limit, dir, unconfirmed) + } + private def getBoxRange(offset: Int, limit: Int): Future[Seq[ModifierId]] = getHistory.map { history => val base: Long = getIndex(GlobalBoxIndexKey, history).getLong - offset @@ -265,21 +307,21 @@ case class BlockchainApiRoute(readersHolder: ActorRef, ergoSettings: ErgoSetting } } - private def getBoxesByErgoTreeUnspent(tree: ErgoTree, offset: Int, limit: Int, sortDir: Direction): Future[Seq[IndexedErgoBox]] = - getHistory.map { history => + private def getBoxesByErgoTreeUnspent(tree: ErgoTree, offset: Int, limit: Int, sortDir: Direction, unconfirmed: Boolean): Future[Seq[IndexedErgoBox]] = + getHistoryWithMempool.map { case (history, mempool) => getAddress(tree)(history) match { - case Some(iEa) => iEa.retrieveUtxos(history, offset, limit, sortDir) - case None => Seq.empty[IndexedErgoBox] + case Some(addr) => addr.retrieveUtxos(history, mempool, offset, limit, sortDir, unconfirmed) + case None => Seq.empty[IndexedErgoBox] } } - private def getBoxesByErgoTreeUnspentR: Route = (post & pathPrefix("box" / "unspent" / "byErgoTree") & ergoTree & paging & sortDir) { (tree, offset, limit, dir) => + private def getBoxesByErgoTreeUnspentR: Route = (post & pathPrefix("box" / "unspent" / "byErgoTree") & ergoTree & paging & sortDir & unconfirmed) { (tree, offset, limit, dir, unconfirmed) => if(limit > MaxItems) { BadRequest(s"No more than $MaxItems boxes can be requested") }else if (dir == SortDirection.INVALID) { - BadRequest("Invalid parameter for sort direction, valid values are \"ASC\" and \"DESC\"") + BadRequest("Invalid parameter for sort direction, valid values are 'ASC' and 'DESC'") }else { - ApiResponse(getBoxesByErgoTreeUnspent(tree, offset, limit, dir)) + ApiResponse(getBoxesByErgoTreeUnspent(tree, offset, limit, dir, unconfirmed)) } } @@ -318,6 +360,14 @@ case class BlockchainApiRoute(readersHolder: ActorRef, ergoSettings: ErgoSetting ApiResponse(getAddressBalanceTotal(address)) } + /** Parses address in the url (i.e. `balanceForAddress/{address}` into [[ErgoAddress]] using [[ErgoAddressEncoder]]. */ + private val addressPass: Directive1[ErgoAddress] = pathPrefix(Segment).flatMap(handleErgoAddress) + + private def getAddressBalanceTotalGetRoute: Route = + (pathPrefix("balanceForAddress") & get & addressPass) { address => + ApiResponse(getAddressBalanceTotal(address)) + } + } object SortDirection { diff --git a/src/main/scala/org/ergoplatform/http/api/ErgoBaseApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/ErgoBaseApiRoute.scala index 498e73fa91..67614c099f 100644 --- a/src/main/scala/org/ergoplatform/http/api/ErgoBaseApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/ErgoBaseApiRoute.scala @@ -97,8 +97,9 @@ trait ErgoBaseApiRoute extends ApiRoute with ApiCodecs { .map { case (utxo: UtxoStateReader, mp: ErgoMemPoolReader) => val maxTxCost = ergoSettings.nodeSettings.maxTransactionCost + val validationContext = utxo.stateContext.simplifiedUpcoming() utxo.withMempool(mp) - .validateWithCost(tx, maxTxCost) + .validateWithCost(tx, validationContext, maxTxCost, None) .map(cost => new UnconfirmedTransaction(tx, Some(cost), now, now, bytes, source = None)) case _ => tx.statelessValidity().map(_ => new UnconfirmedTransaction(tx, None, now, now, bytes, source = None)) diff --git a/src/main/scala/org/ergoplatform/http/api/ErgoPeersApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/ErgoPeersApiRoute.scala index 988b02135a..6efe66f3cf 100644 --- a/src/main/scala/org/ergoplatform/http/api/ErgoPeersApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/ErgoPeersApiRoute.scala @@ -56,7 +56,7 @@ class ErgoPeersApiRoute(peerManager: ActorRef, con.peerInfo.map { peerInfo => PeerInfoResponse( address = peerInfo.peerSpec.declaredAddress.map(_.toString).getOrElse(""), - lastMessage = con.lastMessage, + lastMessage = peerInfo.lastStoredActivityTime, lastHandshake = peerInfo.lastHandshake, name = peerInfo.peerSpec.nodeName, connectionType = peerInfo.connectionType.map(_.toString), diff --git a/src/main/scala/org/ergoplatform/http/api/ErgoUtilsApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/ErgoUtilsApiRoute.scala index 5715e4d5aa..8684526663 100644 --- a/src/main/scala/org/ergoplatform/http/api/ErgoUtilsApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/ErgoUtilsApiRoute.scala @@ -13,7 +13,7 @@ import scorex.core.settings.RESTApiSettings import scorex.core.utils.ScorexEncoding import scorex.crypto.hash.Blake2b256 import scorex.util.encode.Base16 -import sigmastate.basics.DLogProtocol.ProveDlog +import sigmastate.crypto.DLogProtocol.ProveDlog import java.security.SecureRandom import scala.util.Failure diff --git a/src/main/scala/org/ergoplatform/http/api/InfoApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/InfoApiRoute.scala index ce04c7fbe8..e848955fb5 100644 --- a/src/main/scala/org/ergoplatform/http/api/InfoApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/InfoApiRoute.scala @@ -1,6 +1,7 @@ package org.ergoplatform.http.api import akka.actor.{ActorRef, ActorRefFactory} +import akka.http.scaladsl.model.ContentTypes import akka.http.scaladsl.server.Route import akka.pattern.ask import io.circe.syntax._ @@ -16,9 +17,17 @@ case class InfoApiRoute(statsCollector: ActorRef, settings: RESTApiSettings) (implicit val context: ActorRefFactory) extends ErgoBaseApiRoute { - override val route: Route = (path("info") & get) { - val timeJson = Map("currentTime" -> System.currentTimeMillis().asJson).asJson - ApiResponse((statsCollector ? GetNodeInfo).mapTo[NodeInfo].map(_.asJson.deepMerge(timeJson))) + override val route: Route = { + (path("info") & get) { + val timeJson = Map("currentTime" -> System.currentTimeMillis().asJson).asJson + ApiResponse((statsCollector ? GetNodeInfo).mapTo[NodeInfo].map(_.asJson.deepMerge(timeJson))) + } ~ + (path(".well-known" / "ai-plugin.json") & get) { + getFromResource(".well-known/ai-plugin.json", ContentTypes.`application/json`) + } ~ + (path("openapi.yaml") & get) { + getFromResource("api/openapi-ai.yaml", ContentTypes.`text/plain(UTF-8)`) + } } } diff --git a/src/main/scala/org/ergoplatform/http/api/MiningApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/MiningApiRoute.scala index 03e7d543a2..c9ca7c1b8c 100644 --- a/src/main/scala/org/ergoplatform/http/api/MiningApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/MiningApiRoute.scala @@ -13,7 +13,7 @@ import org.ergoplatform.settings.ErgoSettings import org.ergoplatform.{ErgoAddress, ErgoTreePredef, Pay2SAddress} import scorex.core.api.http.ApiResponse import scorex.core.settings.RESTApiSettings -import sigmastate.basics.DLogProtocol.ProveDlog +import sigmastate.crypto.DLogProtocol.ProveDlog import scala.concurrent.Future diff --git a/src/main/scala/org/ergoplatform/http/api/ScanEntities.scala b/src/main/scala/org/ergoplatform/http/api/ScanEntities.scala index 312fcbd5fb..f2ae3f0a17 100644 --- a/src/main/scala/org/ergoplatform/http/api/ScanEntities.scala +++ b/src/main/scala/org/ergoplatform/http/api/ScanEntities.scala @@ -2,7 +2,8 @@ package org.ergoplatform.http.api import io.circe.{Decoder, HCursor} import org.ergoplatform.ErgoBox.BoxId -import org.ergoplatform.{ErgoBox, JsonCodecs} +import org.ergoplatform.ErgoBox +import org.ergoplatform.sdk.JsonCodecs import org.ergoplatform.wallet.Constants.ScanId /** diff --git a/src/main/scala/org/ergoplatform/http/api/ScriptApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/ScriptApiRoute.scala index d7a0bf8f37..87e20bfe74 100644 --- a/src/main/scala/org/ergoplatform/http/api/ScriptApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/ScriptApiRoute.scala @@ -17,7 +17,7 @@ import scorex.core.settings.RESTApiSettings import scorex.util.encode.Base16 import sigmastate.Values.{ByteArrayConstant, ErgoTree} import sigmastate._ -import sigmastate.basics.DLogProtocol.ProveDlog +import sigmastate.crypto.DLogProtocol.ProveDlog import sigmastate.eval.CompiletimeIRContext import sigmastate.interpreter.Interpreter import sigmastate.lang.{CompilerResult, SigmaCompiler} diff --git a/src/main/scala/org/ergoplatform/local/CleanupWorker.scala b/src/main/scala/org/ergoplatform/local/CleanupWorker.scala index 01232ad607..0db0c9fce8 100644 --- a/src/main/scala/org/ergoplatform/local/CleanupWorker.scala +++ b/src/main/scala/org/ergoplatform/local/CleanupWorker.scala @@ -86,7 +86,8 @@ class CleanupWorker(nodeViewHolderRef: ActorRef, ): (mutable.ArrayBuilder[UnconfirmedTransaction], mutable.ArrayBuilder[ModifierId]) = { txs match { case head :: tail if costAcc < CostLimit => - state.validateWithCost(head.transaction, nodeSettings.maxTransactionCost) match { + val validationContext = state.stateContext.simplifiedUpcoming() + state.validateWithCost(head.transaction, validationContext, nodeSettings.maxTransactionCost, None) match { case Success(txCost) => val updTx = head.withCost(txCost) validationLoop(tail, validated += updTx, invalidated, txCost + costAcc) diff --git a/src/main/scala/org/ergoplatform/local/ErgoStatsCollector.scala b/src/main/scala/org/ergoplatform/local/ErgoStatsCollector.scala index febc4eebb3..c171e5ab28 100644 --- a/src/main/scala/org/ergoplatform/local/ErgoStatsCollector.scala +++ b/src/main/scala/org/ergoplatform/local/ErgoStatsCollector.scala @@ -165,7 +165,7 @@ object ErgoStatsCollector { * @param isMining - whether the node is mining * @param bestHeaderOpt - best header ID * @param headersScore - cumulative difficulty of best headers-chain - * @param bestFullBlockOpt - best full-block id (header id of such block) + * @param bestFullBlockOpt - Best full-block known to the node. Can be None if state is empty (no full block is applied since node launch) * @param fullBlocksScore - cumulative difficulty of best full blocks chain * @param maxPeerHeight - maximum block height of connected peers * @param launchTime - when the node was launched (in Java time format, basically, UNIX time * 1000) diff --git a/src/main/scala/org/ergoplatform/mining/AutolykosPowScheme.scala b/src/main/scala/org/ergoplatform/mining/AutolykosPowScheme.scala index 858bc51d7b..ad5af554a6 100644 --- a/src/main/scala/org/ergoplatform/mining/AutolykosPowScheme.scala +++ b/src/main/scala/org/ergoplatform/mining/AutolykosPowScheme.scala @@ -15,7 +15,7 @@ import org.ergoplatform.nodeView.mempool.TransactionMembershipProof import scorex.crypto.authds.{ADDigest, SerializedAdProof} import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.{ModifierId, ScorexLogging} -import sigmastate.basics.DLogProtocol.ProveDlog +import sigmastate.crypto.DLogProtocol.ProveDlog import sigmastate.crypto.CryptoFacade import scala.annotation.tailrec diff --git a/src/main/scala/org/ergoplatform/mining/AutolykosSolution.scala b/src/main/scala/org/ergoplatform/mining/AutolykosSolution.scala index 13917b06a7..adeff25edc 100644 --- a/src/main/scala/org/ergoplatform/mining/AutolykosSolution.scala +++ b/src/main/scala/org/ergoplatform/mining/AutolykosSolution.scala @@ -8,8 +8,8 @@ import org.ergoplatform.modifiers.history.header.Header.Version import org.ergoplatform.settings.Algos import scorex.core.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} -import sigmastate.basics.CryptoConstants -import sigmastate.basics.CryptoConstants.EcPointType +import sigmastate.crypto.CryptoConstants +import sigmastate.crypto.CryptoConstants.EcPointType /** * Solution for an Autolykos PoW puzzle. diff --git a/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala b/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala index 61fee2f123..17ef8af2a9 100644 --- a/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala +++ b/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala @@ -26,13 +26,13 @@ import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, ErgoTreePredef, Input} import scorex.crypto.hash.Digest32 import scorex.util.encode.Base16 import scorex.util.{ModifierId, ScorexLogging} -import sigmastate.SType.ErgoBoxRType -import sigmastate.basics.DLogProtocol.ProveDlog +import sigmastate.ErgoBoxRType +import sigmastate.crypto.DLogProtocol.ProveDlog import sigmastate.crypto.CryptoFacade import sigmastate.eval.Extensions._ import sigmastate.eval._ import sigmastate.interpreter.ProverResult -import special.collection.Coll +import sigma.{Coll, Colls} import scala.annotation.tailrec import scala.concurrent.duration._ @@ -819,7 +819,7 @@ object CandidateGenerator extends ScorexLogging { // check validity and calculate transaction cost stateWithTxs.validateWithCost( tx, - Some(upcomingContext), + upcomingContext, maxBlockCost, Some(verifier) ) match { diff --git a/src/main/scala/org/ergoplatform/mining/DefaultFakePowScheme.scala b/src/main/scala/org/ergoplatform/mining/DefaultFakePowScheme.scala index f318a4f426..732bf25805 100644 --- a/src/main/scala/org/ergoplatform/mining/DefaultFakePowScheme.scala +++ b/src/main/scala/org/ergoplatform/mining/DefaultFakePowScheme.scala @@ -3,7 +3,7 @@ package org.ergoplatform.mining import org.ergoplatform.modifiers.history.header.Header import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Digest32 -import sigmastate.basics.CryptoConstants.EcPointType +import sigmastate.crypto.CryptoConstants.EcPointType import scala.util.{Random, Success, Try} diff --git a/src/main/scala/org/ergoplatform/mining/ErgoMiner.scala b/src/main/scala/org/ergoplatform/mining/ErgoMiner.scala index 887845cd20..4379ebd26b 100644 --- a/src/main/scala/org/ergoplatform/mining/ErgoMiner.scala +++ b/src/main/scala/org/ergoplatform/mining/ErgoMiner.scala @@ -10,7 +10,7 @@ import org.ergoplatform.settings.ErgoSettings import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.GetDataFromCurrentView import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.FullBlockApplied import scorex.util.ScorexLogging -import sigmastate.basics.DLogProtocol.{DLogProverInput, ProveDlog} +import sigmastate.crypto.DLogProtocol.{DLogProverInput, ProveDlog} import scala.concurrent.duration._ import scala.util.{Failure, Success} diff --git a/src/main/scala/org/ergoplatform/mining/WorkMessage.scala b/src/main/scala/org/ergoplatform/mining/WorkMessage.scala index 19d758d961..3c815d44fc 100644 --- a/src/main/scala/org/ergoplatform/mining/WorkMessage.scala +++ b/src/main/scala/org/ergoplatform/mining/WorkMessage.scala @@ -4,7 +4,7 @@ import io.circe.syntax._ import io.circe.{Encoder, Json} import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.nodeView.history.ErgoHistory.Height -import sigmastate.basics.DLogProtocol.ProveDlog +import sigmastate.crypto.DLogProtocol.ProveDlog /** diff --git a/src/main/scala/org/ergoplatform/mining/mining.scala b/src/main/scala/org/ergoplatform/mining/mining.scala index 3fc4be9f3e..526d2eeba2 100644 --- a/src/main/scala/org/ergoplatform/mining/mining.scala +++ b/src/main/scala/org/ergoplatform/mining/mining.scala @@ -2,9 +2,9 @@ package org.ergoplatform import org.bouncycastle.util.BigIntegers import scorex.crypto.hash.Blake2b256 -import sigmastate.basics.CryptoConstants.EcPointType -import sigmastate.basics.{BcDlogGroup, CryptoConstants} -import sigmastate.basics.DLogProtocol.DLogProverInput +import sigmastate.crypto.CryptoConstants.EcPointType +import sigmastate.crypto.{BcDlogGroup, CryptoConstants} +import sigmastate.crypto.DLogProtocol.DLogProverInput import sigmastate.serialization.{GroupElementSerializer, SigmaSerializer} package object mining { diff --git a/src/main/scala/org/ergoplatform/modifiers/history/PreHeader.scala b/src/main/scala/org/ergoplatform/modifiers/history/PreHeader.scala index 8387a14291..da183f33eb 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/PreHeader.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/PreHeader.scala @@ -6,7 +6,7 @@ import org.ergoplatform.modifiers.history.header.Header._ import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.settings.Constants import scorex.util._ -import sigmastate.basics.CryptoConstants.EcPointType +import sigmastate.crypto.CryptoConstants.EcPointType import sigmastate.eval.CGroupElement import sigmastate.eval.Extensions._ @@ -35,7 +35,7 @@ case class CPreHeader(version: Version, object PreHeader { - def toSigma(preHeader: PreHeader): special.sigma.PreHeader = + def toSigma(preHeader: PreHeader): sigma.PreHeader = sigmastate.eval.CPreHeader( version = preHeader.version, parentId = preHeader.parentId.toBytes.toColl, diff --git a/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala b/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala index 27c53bc80b..c402d88342 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala @@ -14,7 +14,7 @@ import scorex.util.ModifierId /** * Extension section of Ergo block. Contains key-value storage - * represented as Seq[(Array[Byte], Array[Byte])] with mandatory and optional fields. + * represented as Seq[(Array[Byte], Array[Byte])] with mandatory (consensus-critical) and optional fields. * * @param headerId - id of corresponding header * @param fields - fields as a sequence of key -> value records. A key is 2-bytes long, value is 64 bytes max. @@ -31,8 +31,8 @@ case class Extension(headerId: ModifierId, override def serializer: ErgoSerializer[Extension] = ExtensionSerializer override def toString: String = { - s"Extension(id: $id, headerId: ${Algos.encode(headerId)}, " + - s"fields: ${fields.map(kv => s"${Algos.encode(kv._1)} -> ${Algos.encode(kv._2)}")}) " + val fieldsEncoded = fields.map(kv => s"${Algos.encode(kv._1)} -> ${Algos.encode(kv._2)}") + s"Extension(id: $id, headerId: $headerId, fields: $fieldsEncoded)" } } @@ -42,20 +42,51 @@ object Extension extends ApiCodecs { val FieldKeySize: Int = 2 val FieldValueMaxSize: Int = 64 - //predefined key prefixes + /** + * Predefined key spaces. Defined by first byte of a key, then there could be up to 256 keys per key space, as + * a key is of 2 bytes always. + */ + + /** + * Every voting epoch length blocks (so every 1024 blocks for the Ergo mainnet, current blockchain parameters are + * written into extension section, to support light clients which do not processing all the blocks but need to know + * current blockchain parameters (i.e. without processing all the historical blocks) to process new blocks. + * + * All the parameters are written into key space defined by the value below. + */ val SystemParametersPrefix: Byte = 0x00 + + /** + * Every block interlinks vector needed in order to build and validate NiPoPoWs is written into extension section, + * + * All the interlinks are written into a single key space defined by the value below. + */ val InterlinksVectorPrefix: Byte = 0x01 + + /** + * It is possible to switch some soft-forkable rules and introduce new ones via 90+% mines voting. Changes in rules + * against the genesis block are to be written into a single key space defined by the value below. + */ val ValidationRulesPrefix: Byte = 0x02 + /** + * Id a type of network object encoding extension + */ + val modifierTypeId: NetworkObjectTypeId.Value = ExtensionTypeId.value + + /** + * @return key-value pair `kv` encoded as Merkle tree leaf (byte array) + */ def kvToLeaf(kv: (Array[Byte], Array[Byte])): Array[Byte] = Bytes.concat(Array(kv._1.length.toByte), kv._1, kv._2) + /** + * @return Merkle tree built on top of key-value pairs + */ def merkleTree(fields: Seq[(Array[Byte], Array[Byte])]): MerkleTree[Digest32] = { Algos.merkleTree(LeafData @@ fields.map(kvToLeaf)) } - val modifierTypeId: NetworkObjectTypeId.Value = ExtensionTypeId.value - implicit val jsonEncoder: Encoder[Extension] = { e: Extension => Map( "headerId" -> Algos.encode(e.headerId).asJson, diff --git a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala b/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala index 2d82a70f74..df31f80686 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala @@ -16,7 +16,7 @@ import scorex.core.serialization.ErgoSerializer import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Digest32 import scorex.util._ -import sigmastate.basics.CryptoConstants.EcPointType +import sigmastate.crypto.CryptoConstants.EcPointType import sigmastate.eval.Extensions._ import sigmastate.eval.{CAvlTree, CBigInt, CGroupElement, CHeader} @@ -136,13 +136,13 @@ object Header extends ApiCodecs { val Interpreter50Version: Byte = 3 - def toSigma(header: Header): special.sigma.Header = + def toSigma(header: Header): sigma.Header = CHeader( id = header.id.toBytes.toColl, version = header.version, parentId = header.parentId.toBytes.toColl, ADProofsRoot = header.ADProofsRoot.asInstanceOf[Array[Byte]].toColl, - stateRoot = CAvlTree(ErgoInterpreter.avlTreeFromDigest(header.stateRoot)), + stateRoot = CAvlTree(ErgoInterpreter.avlTreeFromDigest(header.stateRoot.toColl)), transactionsRoot = header.transactionsRoot.asInstanceOf[Array[Byte]].toColl, timestamp = header.timestamp, nBits = header.nBits, diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 70061fc910..352a41ab70 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -34,12 +34,11 @@ import scorex.core.validation.MalformedModifierError import scorex.util.{ModifierId, ScorexLogging} import scorex.core.network.DeliveryTracker import scorex.core.network.peer.PenaltyType -import scorex.core.transaction.state.TransactionValidation.TooHighCostError -import scorex.core.app.Version import scorex.crypto.hash.Digest32 import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.ErgoLikeContext.Height import scorex.core.serialization.{ErgoSerializer, ManifestSerializer, SubtreeSerializer} +import scorex.core.transaction.TooHighCostError import scorex.crypto.authds.avltree.batch.VersionedLDBAVLStorage.splitDigest import scala.annotation.tailrec @@ -75,8 +74,6 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, Restart } - private val blockSectionsDownloadFilter = BlockSectionsDownloadFilter(settings.nodeSettings.stateType) - private var syncInfoV1CacheByHeadersHeight: Option[(Int, ErgoSyncInfoV1)] = Option.empty private var syncInfoV2CacheByHeadersHeight: Option[(Int, ErgoSyncInfoV2)] = Option.empty @@ -530,7 +527,8 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, * @return available peers to download headers from together with the state/origin of the peer */ private def getPeersForDownloadingHeaders(callingPeer: ConnectedPeer): Iterable[ConnectedPeer] = { - syncTracker.peersByStatus.getOrElse(Older, Array(callingPeer)) + val nonFiltered: Iterable[ConnectedPeer] = syncTracker.peersByStatus.getOrElse(Older, Array(callingPeer)) + HeadersDownloadFilter.filter(nonFiltered) } /** @@ -545,7 +543,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, .orElse { Option(peersByStatus.getOrElse(Unknown, mutable.WrappedArray.empty) ++ peersByStatus.getOrElse(Fork, mutable.WrappedArray.empty)) .filter(_.nonEmpty) - }.map(blockSectionsDownloadFilter.filter) + }.map(BlockSectionsDownloadFilter.filter) } /** @@ -1129,14 +1127,8 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, Seq.empty } case _ => - if (peer.peerInfo.map(_.peerSpec.protocolVersion).getOrElse(Version.initial) == Version.v4043 && - modifierTypeId == Header.modifierTypeId) { - log.debug("Header ids from 4.0.43") - Seq.empty - } else { - log.info(s"Processing ${invData.ids.length} non-tx invs (of type $modifierTypeId) from $peer") - invData.ids.filter(mid => deliveryTracker.status(mid, modifierTypeId, Seq(hr)) == ModifiersStatus.Unknown) - } + log.info(s"Processing ${invData.ids.length} non-tx invs (of type $modifierTypeId) from $peer") + invData.ids.filter(mid => deliveryTracker.status(mid, modifierTypeId, Seq(hr)) == ModifiersStatus.Unknown) } if (newModifierIds.nonEmpty) { @@ -1412,7 +1404,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, utx.source.foreach { peer => // no need to call deliveryTracker.setInvalid, as mempool will consider invalidated tx in contains() error match { - case TooHighCostError(_) => + case TooHighCostError(_, _) => log.info(s"Penalize spamming peer $peer for too costly transaction $id") penalizeSpammingPeer(peer) case _ => diff --git a/src/main/scala/org/ergoplatform/network/ModePeerFeature.scala b/src/main/scala/org/ergoplatform/network/ModePeerFeature.scala index e2f46a596e..b5b47f2556 100644 --- a/src/main/scala/org/ergoplatform/network/ModePeerFeature.scala +++ b/src/main/scala/org/ergoplatform/network/ModePeerFeature.scala @@ -15,36 +15,73 @@ import scorex.util.serialization.{Reader, Writer} * * @param stateType - information on whether UTXO set is store (so state type is UTXO/Digest) * @param verifyingTransactions - whether the peer is verifying transactions - * @param nipopowSuffix - whether the peer has has bootstrapped via Nipopows, and length of proof suffix + * @param nipopowBootstrapped - whether the peer has bootstrapped via Nipopows, + * and if so, supported bootstrapping options (only one currently) * @param blocksToKeep - how many last full blocks the peer is storing */ case class ModePeerFeature(stateType: StateType, verifyingTransactions: Boolean, - nipopowSuffix: Option[Int], + nipopowBootstrapped: Option[Int], blocksToKeep: Int) extends PeerFeature { override type M = ModePeerFeature override val featureId: Id = PeerFeatureDescriptors.ModeFeatureId override def serializer: ErgoSerializer[ModePeerFeature] = ModeFeatureSerializer + + /** + * @return whether the peer has all the full blocks + */ + def allBlocksAvailable: Boolean = blocksToKeep == ModePeerFeature.AllBlocksKept + + + /** + * @return whether the peer has all the headers + */ + def allHeadersAvailable: Boolean = nipopowBootstrapped.isEmpty } object ModePeerFeature { import io.circe.syntax._ + /** + * Flag which is indicating NiPoPoW bootstrap mode. Currently there is only one option (suffix proof done + * according to KMZ17 paper), which does not exclude possibility for more options in future + */ + val NiPoPoWDefaultFlag = 1 + + /** + * Flag value which is when used as length of blockchain suffix kept locally means that all the full blocks are + * stored + */ + val AllBlocksKept = -1 + + /** + * Flag value which is when used as length of blockchain suffix kept locally means that a node was bootstrapped + * via UTXO set snapshot, so not all the full blocks stored, but at the same time there is no fixed-length suffix + * as after bootstrapping there is no pruning + */ + val UTXOSetBootstrapped = -2 + def apply(nodeSettings: NodeConfigurationSettings): ModePeerFeature = { - val popowSuffix = if (nodeSettings.nipopowSettings.nipopowBootstrap) { - Some(nodeSettings.nipopowSettings.nipopowSuffix) + val popowBootstrapped = if (nodeSettings.nipopowSettings.nipopowBootstrap) { + Some(NiPoPoWDefaultFlag) } else { None } + val blocksKept = if (nodeSettings.utxoSettings.utxoBootstrap) { + UTXOSetBootstrapped + } else { + nodeSettings.blocksToKeep + } + new ModePeerFeature( nodeSettings.stateType, nodeSettings.verifyTransactions, - popowSuffix, - nodeSettings.blocksToKeep + popowBootstrapped, + blocksKept ) } @@ -76,8 +113,8 @@ object ModeFeatureSerializer extends ErgoSerializer[ModePeerFeature] { override def serialize(mf: ModePeerFeature, w: Writer): Unit = { w.put(mf.stateType.stateTypeCode) w.put(booleanToByte(mf.verifyingTransactions)) - w.putOption(mf.nipopowSuffix)(_.putInt(_)) - w.putInt(mf.blocksToKeep) // todo: put -2 if bootstrapped via utxo set snapshot? https://github.com/ergoplatform/ergo/issues/2014 + w.putOption(mf.nipopowBootstrapped)(_.putInt(_)) + w.putInt(mf.blocksToKeep) } override def parse(r: Reader): ModePeerFeature = { @@ -85,13 +122,13 @@ object ModeFeatureSerializer extends ErgoSerializer[ModePeerFeature] { val stateType = StateType.fromCode(r.getByte()) val verifyingTransactions = byteToBoolean(r.getByte()) - val popowSuffix = r.getOption(r.getInt()) + val popowBootstrapped = r.getOption(r.getInt()) val blocksToKeep = r.getInt() new ModePeerFeature( stateType, verifyingTransactions, - popowSuffix, + popowBootstrapped, blocksToKeep ) } diff --git a/src/main/scala/org/ergoplatform/network/VersionBasedPeerFilteringRule.scala b/src/main/scala/org/ergoplatform/network/VersionBasedPeerFilteringRule.scala index c7499c878e..ed831e470e 100644 --- a/src/main/scala/org/ergoplatform/network/VersionBasedPeerFilteringRule.scala +++ b/src/main/scala/org/ergoplatform/network/VersionBasedPeerFilteringRule.scala @@ -1,6 +1,5 @@ package org.ergoplatform.network -import org.ergoplatform.nodeView.state.StateType import scorex.core.app.Version import scorex.core.network.ConnectedPeer @@ -48,44 +47,6 @@ trait VersionBasedPeerFilteringRule extends PeerFilteringRule { } - -/** - * 4.0.22+ allow for downloading ADProofs that are too big in block at 667614 - * for prior versions, a peer will not deliver block # 667614 and some other blocks - */ -object DigestModeFilter extends VersionBasedPeerFilteringRule { - - override def condition(version: Version): Boolean = { - version.compare(Version.v4022) >= 0 - } - -} - -/** - * Filter out peers of 4.0.17 or 4.0.18 version as they are delivering broken block sections - */ -object BrokenModifiersFilter extends VersionBasedPeerFilteringRule { - - override def condition(version: Version): Boolean = { - version != Version.v4017 && version != Version.v4018 - } - -} - -/** - * Filter to download block sections, combining `DigestModeFilter` and `BrokenModifiersFilter` - * @param stateType - own (node's) state type - */ -final case class BlockSectionsDownloadFilter(stateType: StateType) extends VersionBasedPeerFilteringRule { - override def condition(version: Version): Boolean = { - if (stateType == StateType.Digest) { - DigestModeFilter.condition(version) - } else { - BrokenModifiersFilter.condition(version) - } - } -} - /** * If peer's version is >= 4.0.16, the peer is supporting sync V2 */ @@ -125,8 +86,29 @@ object NipopowSupportFilter extends PeerFilteringRule { override def condition(peer: ConnectedPeer): Boolean = { val version = peer.peerInfo.map(_.peerSpec.protocolVersion).getOrElse(Version.initial) - peer.mode.flatMap(_.nipopowSuffix).isEmpty && + peer.mode.flatMap(_.nipopowBootstrapped).isEmpty && version.compare(Version.NipopowActivationVersion) >= 0 } } + +/** + * Filter to download block sections (except of headers). + * Currently, it is accepting peers which do have all the full blocks (so not bootstrapped via UTXO set snapshot + * or stateless client with full blocks suffix) + */ +object BlockSectionsDownloadFilter extends PeerFilteringRule { + override def condition(peer: ConnectedPeer): Boolean = { + peer.mode.exists(_.allBlocksAvailable) + } +} + +/** + * Filter to download headers. + * Currently, it is accepting peers which do have all the headers (so not bootstrapped via NiPoPoWs. + */ +object HeadersDownloadFilter extends PeerFilteringRule { + override def condition(peer: ConnectedPeer): Boolean = { + peer.mode.exists(_.allHeadersAvailable) + } +} diff --git a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala index 8c80963471..7dfd9c9ba4 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala @@ -445,7 +445,8 @@ trait ErgoHistoryReader (typedModifierById[BlockTransactions](header.transactionsId), typedModifierById[Extension](header.extensionId), typedModifierById[ADProofs](header.ADProofsId)) match { - case (Some(txs), Some(ext), Some(proofs)) => Some(ErgoFullBlock(header, txs, ext, Some(proofs))) + case (Some(txs), Some(ext), Some(proofs)) => + Some(ErgoFullBlock(header, txs, ext, Some(proofs))) case (Some(txs), Some(ext), None) if !nodeSettings.stateType.requireProofs => Some(ErgoFullBlock(header, txs, ext, None)) case _ => None diff --git a/src/main/scala/org/ergoplatform/nodeView/history/extra/BalanceInfo.scala b/src/main/scala/org/ergoplatform/nodeView/history/extra/BalanceInfo.scala index a6002c1b0c..67a6fb1a1d 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/BalanceInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/BalanceInfo.scala @@ -8,7 +8,7 @@ import scorex.core.serialization.ErgoSerializer import scorex.util.{ModifierId, ScorexLogging, bytesToId} import scorex.util.serialization.{Reader, Writer} import spire.implicits.cfor -import special.collection.Extensions._ +import sigma.Extensions._ import scala.collection.mutable import scala.collection.mutable.ArrayBuffer diff --git a/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoAddress.scala b/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoAddress.scala index 0965922603..8abd05bba1 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoAddress.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoAddress.scala @@ -5,7 +5,8 @@ import org.ergoplatform.http.api.SortDirection.{ASC, DESC, Direction} import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoHistoryReader} import org.ergoplatform.nodeView.history.extra.ExtraIndexer.{ExtraIndexTypeId, fastIdToBytes} import org.ergoplatform.nodeView.history.extra.IndexedErgoAddress.{getBoxes, getFromSegments, getTxs} -import org.ergoplatform.nodeView.history.extra.IndexedErgoAddressSerializer.{boxSegmentId, txSegmentId} +import org.ergoplatform.nodeView.history.extra.IndexedErgoAddressSerializer.{boxSegmentId, hashErgoTree, txSegmentId} +import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader import org.ergoplatform.settings.Algos import scorex.core.serialization.ErgoSerializer import scorex.util.{ModifierId, ScorexLogging, bytesToId} @@ -141,14 +142,21 @@ case class IndexedErgoAddress(treeHash: ModifierId, /** * Get a range of the boxes associated with this address that are NOT spent * @param history - history to use + * @param mempool - mempool to use, if unconfirmed is true * @param offset - items to skip from the start * @param limit - items to retrieve * @param sortDir - whether to start retreival from newest box ([[DESC]]) or oldest box ([[ASC]]) + * @param unconfirmed - whether to include unconfirmed boxes * @return array of unspent boxes */ - def retrieveUtxos(history: ErgoHistoryReader, offset: Int, limit: Int, sortDir: Direction): Array[IndexedErgoBox] = { + def retrieveUtxos(history: ErgoHistoryReader, + mempool: ErgoMemPoolReader, + offset: Int, + limit: Int, + sortDir: Direction, + unconfirmed: Boolean): Seq[IndexedErgoBox] = { val data: ArrayBuffer[IndexedErgoBox] = ArrayBuffer.empty[IndexedErgoBox] - sortDir match { + val confirmedBoxes: Seq[IndexedErgoBox] = sortDir match { case DESC => data ++= boxes.filter(_ > 0).map(n => NumericBoxIndex.getBoxByNumber(history, n).get) var segment: Int = boxSegmentCount @@ -157,7 +165,7 @@ case class IndexedErgoAddress(treeHash: ModifierId, history.typedExtraIndexById[IndexedErgoAddress](boxSegmentId(treeHash, segment)).get.boxes .filter(_ > 0).map(n => NumericBoxIndex.getBoxByNumber(history, n).get) ++=: data } - data.reverse.slice(offset, offset + limit).toArray + data.reverse.slice(offset, offset + limit) case ASC => var segment: Int = 0 while (data.length < (limit + offset) && segment < boxSegmentCount) { @@ -167,8 +175,18 @@ case class IndexedErgoAddress(treeHash: ModifierId, } if(data.length < (limit + offset)) data ++= boxes.filter(_ > 0).map(n => NumericBoxIndex.getBoxByNumber(history, n).get) - data.slice(offset, offset + limit).toArray + data.slice(offset, offset + limit) } + if(unconfirmed) { + val mempoolBoxes = mempool.getAll.flatMap(_.transaction.outputs) + .filter(box => hashErgoTree(box.ergoTree) == treeHash) + val unconfirmedBoxes = mempoolBoxes.map(new IndexedErgoBox(0, None, None, _, 0)) + sortDir match { + case DESC => unconfirmedBoxes ++ confirmedBoxes + case ASC => confirmedBoxes ++ unconfirmedBoxes + } + } else + confirmedBoxes } /** diff --git a/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedToken.scala b/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedToken.scala index 19c6d18d8e..c87a4fc600 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedToken.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedToken.scala @@ -10,7 +10,7 @@ import scorex.util.{ByteArrayOps, ModifierId, bytesToId} import scorex.util.serialization.{Reader, Writer} import sigmastate.Values.CollectionConstant import sigmastate.SByte -import special.collection.Extensions._ +import sigma.Extensions._ /** * Index of a token containing creation information. diff --git a/src/main/scala/org/ergoplatform/nodeView/mempool/ErgoMemPool.scala b/src/main/scala/org/ergoplatform/nodeView/mempool/ErgoMemPool.scala index 8e5431c369..75f626db88 100644 --- a/src/main/scala/org/ergoplatform/nodeView/mempool/ErgoMemPool.scala +++ b/src/main/scala/org/ergoplatform/nodeView/mempool/ErgoMemPool.scala @@ -6,7 +6,6 @@ import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransacti import org.ergoplatform.nodeView.mempool.OrderedTxPool.WeightedTxId import org.ergoplatform.nodeView.state.{ErgoState, UtxoState} import org.ergoplatform.settings.{ErgoSettings, MonetarySettings, NodeConfigurationSettings} -import scorex.core.transaction.state.TransactionValidation import scorex.util.{ModifierId, ScorexLogging, bytesToId} import OrderedTxPool.weighted import org.ergoplatform.nodeView.mempool.ErgoMemPool.SortingOption @@ -228,7 +227,8 @@ class ErgoMemPool private[mempool](private[mempool] val pool: OrderedTxPool, // Allow proceeded transaction to spend outputs of pooled transactions. val utxoWithPool = utxo.withUnconfirmedTransactions(getAll) if (tx.inputIds.forall(inputBoxId => utxoWithPool.boxById(inputBoxId).isDefined)) { - utxoWithPool.validateWithCost(tx, Some(utxo.stateContext), costLimit, None) match { + val validationContext = utxo.stateContext.simplifiedUpcoming() + utxoWithPool.validateWithCost(tx, validationContext, costLimit, None) match { case Success(cost) => acceptIfNoDoubleSpend(unconfirmedTx.withCost(cost), validationStartTime) case Failure(ex) => @@ -238,15 +238,6 @@ class ErgoMemPool private[mempool](private[mempool] val pool: OrderedTxPool, val exc = new Exception("not all utxos in place yet") this -> new ProcessingOutcome.Declined(exc, validationStartTime) } - case validator: TransactionValidation => - // transaction validation currently works only for UtxoState, so this branch currently - // will not be triggered probably - validator.validateWithCost(tx, costLimit) match { - case Success(cost) => - acceptIfNoDoubleSpend(unconfirmedTx.withCost(cost), validationStartTime) - case Failure(ex) => - this.invalidate(unconfirmedTx) -> new ProcessingOutcome.Invalidated(ex, validationStartTime) - } case _ => // Accept transaction in case of "digest" state. Transactions are not downloaded in this mode from other // peers though, so such transactions can come from the local wallet only. diff --git a/src/main/scala/org/ergoplatform/nodeView/mempool/TransactionMembershipProof.scala b/src/main/scala/org/ergoplatform/nodeView/mempool/TransactionMembershipProof.scala index 1977571e89..2a93fefb83 100644 --- a/src/main/scala/org/ergoplatform/nodeView/mempool/TransactionMembershipProof.scala +++ b/src/main/scala/org/ergoplatform/nodeView/mempool/TransactionMembershipProof.scala @@ -1,7 +1,7 @@ package org.ergoplatform.nodeView.mempool import io.circe.{Encoder, Json} -import org.ergoplatform.JsonCodecs +import org.ergoplatform.sdk.JsonCodecs import org.ergoplatform.settings.Algos import scorex.crypto.authds.merkle.MerkleProof import scorex.crypto.hash.Digest32 diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala index dc3644897f..4290d96ecc 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala @@ -24,7 +24,7 @@ import scorex.util.encode.Base16 import scorex.util.{ModifierId, ScorexLogging, bytesToId} import sigmastate.AtLeast import sigmastate.Values.{ByteArrayConstant, ErgoTree, IntConstant, SigmaPropConstant} -import sigmastate.basics.DLogProtocol.ProveDlog +import sigmastate.crypto.DLogProtocol.ProveDlog import sigmastate.serialization.ValueSerializer import spire.syntax.all.cfor diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala index b6495e3815..ce478458ad 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala @@ -8,7 +8,7 @@ import org.ergoplatform.modifiers.history.header.{Header, HeaderSerializer} import org.ergoplatform.modifiers.history.popow.NipopowAlgos import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.history.storage.modifierprocessors.ExtensionValidator -import org.ergoplatform.sdk.wallet.protocol.context.ErgoLikeStateContext +import org.ergoplatform.sdk.wallet.protocol.context.BlockchainStateContext import org.ergoplatform.settings.ValidationRules._ import org.ergoplatform.settings._ import scorex.core.serialization.{BytesSerializable, ErgoSerializer} @@ -17,9 +17,10 @@ import scorex.core.validation.{InvalidModifier, ModifierValidator, ValidationSta import scorex.crypto.authds.ADDigest import scorex.util.ScorexLogging import scorex.util.serialization.{Reader, Writer} -import sigmastate.basics.CryptoConstants.EcPointType +import sigmastate.crypto.CryptoConstants.EcPointType +import sigmastate.eval.Extensions.ArrayOps import sigmastate.eval.SigmaDsl -import special.collection.Coll +import sigma.Coll import scala.util.{Failure, Success, Try} @@ -37,9 +38,9 @@ case class UpcomingStateContext(override val lastHeaders: Seq[Header], extends ErgoStateContext(lastHeaders, lastExtensionOpt, genesisStateDigest, currentParameters, validationSettings, votingData)(ergoSettings) { - override def sigmaPreHeader: special.sigma.PreHeader = PreHeader.toSigma(predictedHeader) + override def sigmaPreHeader: sigma.PreHeader = PreHeader.toSigma(predictedHeader) - override def sigmaLastHeaders: Coll[special.sigma.Header] = { + override def sigmaLastHeaders: Coll[sigma.Header] = { SigmaDsl.Colls.fromArray(lastHeaders.map(h => Header.toSigma(h)).toArray) } @@ -65,7 +66,7 @@ class ErgoStateContext(val lastHeaders: Seq[Header], val validationSettings: ErgoValidationSettings, val votingData: VotingData) (implicit val ergoSettings: ErgoSettings) - extends ErgoLikeStateContext + extends BlockchainStateContext with BytesSerializable with ScorexEncoding with ScorexLogging { @@ -75,18 +76,18 @@ class ErgoStateContext(val lastHeaders: Seq[Header], private val votingSettings = ergoSettings.chainSettings.voting private val popowAlgos = new NipopowAlgos(ergoSettings.chainSettings) - override def sigmaPreHeader: special.sigma.PreHeader = + override def sigmaPreHeader: sigma.PreHeader = PreHeader.toSigma(lastHeaders.headOption.getOrElse(PreHeader.fake)) - override def sigmaLastHeaders: Coll[special.sigma.Header] = + override def sigmaLastHeaders: Coll[sigma.Header] = SigmaDsl.Colls.fromArray(lastHeaders.drop(1).map(h => Header.toSigma(h)).toArray) // todo remove from ErgoLikeContext and from ErgoStateContext // State root hash before the last block - override def previousStateDigest: ADDigest = if (sigmaLastHeaders.toArray.nonEmpty) { - ADDigest @@ sigmaLastHeaders.toArray.head.stateRoot.digest.toArray + override def previousStateDigest: Coll[Byte] = if (sigmaLastHeaders.toArray.nonEmpty) { + sigmaLastHeaders.toArray.head.stateRoot.digest } else { - genesisStateDigest + genesisStateDigest.toColl } /* NOHF PROOF: @@ -108,6 +109,9 @@ class ErgoStateContext(val lastHeaders: Seq[Header], override def serializer: ErgoSerializer[M] = ErgoStateContextSerializer(ergoSettings) + /** + * @return state context corresponding to a block after last known one with fields provided + */ def upcoming(minerPk: EcPointType, timestamp: Long, nBits: Long, @@ -123,6 +127,25 @@ class ErgoStateContext(val lastHeaders: Seq[Header], calculatedValidationSettings, votingData) } + /** + * @return state context corresponding to a block after last known one + * with fields filled with (kind of) default values + */ + def simplifiedUpcoming(): UpcomingStateContext = { + val minerPk = org.ergoplatform.mining.group.generator + val version = lastHeaderOpt.map(_.version).getOrElse(Header.InitialVersion) + val nBits = lastHeaderOpt.map(_.nBits).getOrElse(ergoSettings.chainSettings.initialNBits) + val timestamp = lastHeaderOpt.map(_.timestamp + 1).getOrElse(System.currentTimeMillis()) + val votes = Array.emptyByteArray + val proposedUpdate = ErgoValidationSettingsUpdate.empty + val upcomingHeader = PreHeader(lastHeaderOpt, version, minerPk, timestamp, nBits, votes) + val height = ErgoHistory.heightOf(lastHeaderOpt) + 1 + val (calculatedParams, updated) = currentParameters.update(height, forkVote = false, votingData.epochVotes, proposedUpdate, votingSettings) + val calculatedValidationSettings = validationSettings.updated(updated) + UpcomingStateContext(lastHeaders, lastExtensionOpt, upcomingHeader, genesisStateDigest, calculatedParams, + calculatedValidationSettings, votingData) + } + protected def checkForkVote(height: Height): Unit = { if (currentParameters.softForkStartingHeight.nonEmpty) { val startingHeight = currentParameters.softForkStartingHeight.get @@ -274,7 +297,7 @@ class ErgoStateContext(val lastHeaders: Seq[Header], }.flatten override def toString: String = - s"ErgoStateContext($currentHeight, ${encoder.encode(previousStateDigest)}, $lastHeaders, $currentParameters)" + s"ErgoStateContext($currentHeight, ${encoder.encode(previousStateDigest.toArray)}, $lastHeaders, $currentParameters)" /** diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index 37003d4241..a4254f07b0 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -13,7 +13,6 @@ import org.ergoplatform.settings.{Algos, ErgoSettings, Parameters} import org.ergoplatform.utils.LoggingUtil import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedModifier import scorex.core._ -import scorex.core.transaction.state.TransactionValidation import scorex.core.utils.ScorexEncoding import scorex.core.validation.ModifierValidator import scorex.crypto.authds.avltree.batch._ @@ -38,7 +37,6 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 override val store: LDBVersionedStore, override protected val ergoSettings: ErgoSettings) extends ErgoState[UtxoState] - with TransactionValidation with UtxoStateReader with ScorexEncoding { diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala index b856643302..f717464185 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala @@ -9,8 +9,7 @@ import org.ergoplatform.settings.{Algos, ErgoSettings} import org.ergoplatform.settings.Algos.HF import org.ergoplatform.wallet.boxes.ErgoBoxSerializer import org.ergoplatform.wallet.interpreter.ErgoInterpreter -import scorex.core.transaction.state.TransactionValidation -import scorex.core.transaction.state.TransactionValidation.TooHighCostError +import scorex.core.transaction.TooHighCostError import scorex.core.validation.MalformedModifierError import scorex.crypto.authds.avltree.batch.{Lookup, PersistentBatchAVLProver, VersionedLDBAVLStorage} import scorex.crypto.authds.{ADDigest, ADKey, SerializedAdProof} @@ -23,7 +22,7 @@ import scala.util.{Failure, Success, Try} * state representation (so functions to generate UTXO set modifiction proofs, do stateful transaction validation, * get UTXOs are there */ -trait UtxoStateReader extends ErgoStateReader with UtxoSetSnapshotPersistence with TransactionValidation { +trait UtxoStateReader extends ErgoStateReader with UtxoSetSnapshotPersistence { protected implicit val hf: HF = Algos.hash @@ -46,10 +45,9 @@ trait UtxoStateReader extends ErgoStateReader with UtxoSetSnapshotPersistence wi * or state context from the previous block if not */ def validateWithCost(tx: ErgoTransaction, - stateContextOpt: Option[ErgoStateContext], + context: ErgoStateContext, costLimit: Int, interpreterOpt: Option[ErgoInterpreter]): Try[Int] = { - val context = stateContextOpt.getOrElse(stateContext) val parameters = context.currentParameters.withBlockCost(costLimit) val verifier = interpreterOpt.getOrElse(ErgoInterpreter(parameters)) @@ -61,27 +59,16 @@ trait UtxoStateReader extends ErgoStateReader with UtxoSetSnapshotPersistence wi context, accumulatedCost = 0L)(verifier) match { case Success(txCost) if txCost > costLimit => - Failure(TooHighCostError(s"Transaction $tx has too high cost $txCost")) + Failure(TooHighCostError(tx, Some(txCost))) case Success(txCost) => Success(txCost) case Failure(mme: MalformedModifierError) if mme.message.contains("CostLimitException") => - Failure(TooHighCostError(s"Transaction $tx has too high cost")) + Failure(TooHighCostError(tx, None)) case f: Failure[_] => f } } } - /** - * Validate transaction as if it was included at the end of the last block. - * This validation does not guarantee that transaction will be valid in future - * as soon as state (both UTXO set and state context) will change. - * - * Used in mempool. - */ - override def validateWithCost(tx: ErgoTransaction, maxTxCost: Int): Try[Int] = { - validateWithCost(tx, None, maxTxCost, None) - } - /** * * @param fb - ergo full block diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala index 0d9732964d..3c1594e091 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala @@ -25,7 +25,7 @@ import scorex.core.VersionTag import scorex.core.utils.ScorexEncoding import scorex.util.{ModifierId, ScorexLogging} import sigmastate.Values.SigmaBoolean -import sigmastate.basics.DLogProtocol.{DLogProverInput, ProveDlog} +import sigmastate.crypto.DLogProtocol.{DLogProverInput, ProveDlog} import scala.concurrent.duration._ import scala.util.{Failure, Success, Try} diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletReader.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletReader.scala index dab82fe87b..fd2d38ba76 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletReader.scala @@ -20,7 +20,7 @@ import org.ergoplatform.{ErgoBox, P2PKAddress} import scorex.core.NodeViewComponent import scorex.util.ModifierId import sigmastate.Values.SigmaBoolean -import sigmastate.basics.DLogProtocol.DLogProverInput +import sigmastate.crypto.DLogProtocol.DLogProverInput import java.util.concurrent.TimeUnit import scala.concurrent.Future diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletService.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletService.scala index 34c82fcded..7ff6ab2d72 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletService.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletService.scala @@ -24,8 +24,8 @@ import org.ergoplatform.wallet.utils.FileUtils import scorex.util.encode.Base16 import scorex.util.{ModifierId} import sigmastate.Values.SigmaBoolean -import sigmastate.basics.DLogProtocol.DLogProverInput -import special.collection.Extensions.CollBytesOps +import sigmastate.crypto.DLogProtocol.DLogProverInput +import sigma.Extensions.CollBytesOps import java.io.FileNotFoundException import scala.collection.compat.immutable.ArraySeq diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala index 669f69da39..247c6cd116 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala @@ -20,8 +20,9 @@ import org.ergoplatform.wallet.interpreter.ErgoProvingInterpreter import org.ergoplatform.wallet.mnemonic.Mnemonic import org.ergoplatform.wallet.transactions.TransactionBuilder import scorex.util.ScorexLogging +import sigma.Colls import sigmastate.Values.ByteArrayConstant -import sigmastate.basics.DLogProtocol.ProveDlog +import sigmastate.crypto.DLogProtocol.ProveDlog import sigmastate.eval.Extensions._ import sigmastate.eval._ import sigmastate.utils.Extensions._ diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/models/CollectedBoxes.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/models/CollectedBoxes.scala index a8940abe55..58fb14001c 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/models/CollectedBoxes.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/models/CollectedBoxes.scala @@ -3,7 +3,8 @@ package org.ergoplatform.nodeView.wallet.models import io.circe.generic.encoding.DerivedObjectEncoder.deriveEncoder import io.circe.syntax._ import io.circe.{Encoder, Json} -import org.ergoplatform.{ErgoBox, JsonCodecs} +import org.ergoplatform.ErgoBox +import org.ergoplatform.sdk.JsonCodecs /** * Response for requested boxes that contains ErgoBoxes and ChangeBoxes diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicate.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicate.scala index 402475a8aa..133c1736ef 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicate.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicate.scala @@ -3,7 +3,7 @@ package org.ergoplatform.nodeView.wallet.scanning import org.ergoplatform.ErgoBox import sigmastate.Values.EvaluatedValue import sigmastate.{SType, Values} -import special.collection.Extensions._ +import sigma.Extensions._ /** * Basic interface for box scanning predicate functionality diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateJsonCodecs.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateJsonCodecs.scala index e8305fb331..c57b4923ff 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateJsonCodecs.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateJsonCodecs.scala @@ -7,7 +7,7 @@ import org.ergoplatform.ErgoBox.RegisterId import org.ergoplatform.http.api.ApiCodecs import sigmastate.SType import sigmastate.Values.EvaluatedValue -import special.collection.Extensions._ +import sigma.Extensions._ object ScanningPredicateJsonCodecs extends ApiCodecs { diff --git a/src/main/scala/org/ergoplatform/reemission/ReemissionRules.scala b/src/main/scala/org/ergoplatform/reemission/ReemissionRules.scala index 7dc87a66b0..2660693fac 100644 --- a/src/main/scala/org/ergoplatform/reemission/ReemissionRules.scala +++ b/src/main/scala/org/ergoplatform/reemission/ReemissionRules.scala @@ -9,7 +9,7 @@ import sigmastate.SBoolean import sigmastate.Values.Value import sigmastate.eval.CompiletimeIRContext import sigmastate.lang.{CompilerSettings, SigmaCompiler, TransformingSigmaBuilder} -import special.collection.Coll +import sigma.Coll import scala.util.Try diff --git a/src/main/scala/org/ergoplatform/settings/ErgoSettings.scala b/src/main/scala/org/ergoplatform/settings/ErgoSettings.scala index 5ceff6e42a..41bdd67a43 100644 --- a/src/main/scala/org/ergoplatform/settings/ErgoSettings.scala +++ b/src/main/scala/org/ergoplatform/settings/ErgoSettings.scala @@ -13,7 +13,7 @@ import org.ergoplatform.{ErgoAddressEncoder, ErgoApp, P2PKAddress} import scorex.core.settings.{ScorexSettings, SettingsReaders} import scorex.util.ScorexLogging import scorex.util.encode.Base16 -import sigmastate.basics.DLogProtocol.ProveDlog +import sigmastate.crypto.DLogProtocol.ProveDlog import java.net.{InetAddress, URL} import scala.util.Try diff --git a/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala b/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala index 1bd729b143..d1b60b5518 100644 --- a/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala +++ b/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala @@ -42,7 +42,7 @@ trait UtxoSettingsReader { /** * Settings related to headers-chain bootstrapping with NiPoPoWs. See ergo.node.nipopow section for settings description. */ -case class NipopowSettings(nipopowBootstrap: Boolean, p2pNipopows: Int, nipopowSuffix: Int) +case class NipopowSettings(nipopowBootstrap: Boolean, p2pNipopows: Int) /** * Custom settings reader for `NipopowSettings` @@ -51,8 +51,7 @@ trait NipopowSettingsReader { implicit val nipopowSettingsReader: ValueReader[NipopowSettings] = { (cfg, path) => NipopowSettings( cfg.as[Boolean](s"$path.nipopowBootstrap"), - cfg.as[Int](s"$path.p2pNipopows"), - cfg.as[Int](s"$path.nipopowSuffix") + cfg.as[Int](s"$path.p2pNipopows") ) } } diff --git a/src/main/scala/org/ergoplatform/settings/Parameters.scala b/src/main/scala/org/ergoplatform/settings/Parameters.scala index f2f896335c..5a12fa813d 100644 --- a/src/main/scala/org/ergoplatform/settings/Parameters.scala +++ b/src/main/scala/org/ergoplatform/settings/Parameters.scala @@ -12,7 +12,7 @@ import scala.util.Try import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.modifiers.history.extension.{Extension, ExtensionCandidate} import Extension.SystemParametersPrefix -import org.ergoplatform.sdk.wallet.protocol.context.ErgoLikeParameters +import org.ergoplatform.sdk.BlockchainParameters /** * System parameters which could be readjusted via collective miners decision. @@ -20,7 +20,7 @@ import org.ergoplatform.sdk.wallet.protocol.context.ErgoLikeParameters class Parameters(val height: Height, val parametersTable: Map[Byte, Int], val proposedUpdate: ErgoValidationSettingsUpdate) - extends ErgoLikeParameters { + extends BlockchainParameters { import Parameters._ diff --git a/src/main/scala/org/ergoplatform/settings/ReemissionSettings.scala b/src/main/scala/org/ergoplatform/settings/ReemissionSettings.scala index 1acc476597..aa869754a9 100644 --- a/src/main/scala/org/ergoplatform/settings/ReemissionSettings.scala +++ b/src/main/scala/org/ergoplatform/settings/ReemissionSettings.scala @@ -6,7 +6,7 @@ import org.ergoplatform.wallet.boxes.ErgoBoxSerializer import scorex.util.ModifierId import scorex.util.encode.Base16 import sigmastate.utils.Extensions.ModifierIdOps -import special.collection.Coll +import sigma.Coll /** * Configuration section for re-emission (EIP27) parameters * diff --git a/src/main/scala/org/ergoplatform/utils/BoxUtils.scala b/src/main/scala/org/ergoplatform/utils/BoxUtils.scala index f6655aca85..37931c9c7f 100644 --- a/src/main/scala/org/ergoplatform/utils/BoxUtils.scala +++ b/src/main/scala/org/ergoplatform/utils/BoxUtils.scala @@ -1,13 +1,12 @@ package org.ergoplatform.utils -import org.ergoplatform.ErgoBox.{NonMandatoryRegisterId, TokenId} -import org.ergoplatform.{ErgoBox, ErgoBoxCandidate} +import org.ergoplatform.ErgoBox.{AdditionalRegisters, TokenId} import org.ergoplatform.settings.{Algos, Parameters} +import org.ergoplatform.{ErgoBox, ErgoBoxCandidate} import scorex.util.ModifierId -import sigmastate.SType -import sigmastate.Values.{ErgoTree, EvaluatedValue} +import sigma.{Coll, Colls} +import sigmastate.Values.ErgoTree import sigmastate.eval._ -import special.collection.Coll object BoxUtils { @@ -19,7 +18,7 @@ object BoxUtils { @inline def minimalErgoAmountSimulated(script: ErgoTree, tokens: Coll[(TokenId, Long)] = Colls.emptyColl, - additionalRegisters: Map[NonMandatoryRegisterId, _ <: EvaluatedValue[_ <: SType]] = Map(), + additionalRegisters: AdditionalRegisters = Map(), parameters: Parameters): Long = { val candidateMock = new ErgoBoxCandidate(value = Long.MaxValue, script, creationHeight = Int.MaxValue, tokens, additionalRegisters) val mockId = ModifierId @@ Algos.encode(scorex.util.Random.randomBytes(32)) diff --git a/src/main/scala/scorex/core/app/Version.scala b/src/main/scala/scorex/core/app/Version.scala index 5ca27569e6..197683bdb6 100644 --- a/src/main/scala/scorex/core/app/Version.scala +++ b/src/main/scala/scorex/core/app/Version.scala @@ -33,14 +33,6 @@ object Version { val initial: Version = Version(0, 0, 1) - val v4017: Version = Version(4, 0, 17) - - val v4018: Version = Version(4, 0, 18) - - val v4022: Version = Version(4, 0, 22) - - val v4043: Version = Version(4, 0, 43) - val Eip37ForkVersion: Version = Version(4, 0, 100) val JitSoftForkVersion: Version = Version(5, 0, 0) diff --git a/src/main/scala/scorex/core/network/ConnectedPeer.scala b/src/main/scala/scorex/core/network/ConnectedPeer.scala index 55ac009a17..711e6b89b6 100644 --- a/src/main/scala/scorex/core/network/ConnectedPeer.scala +++ b/src/main/scala/scorex/core/network/ConnectedPeer.scala @@ -15,7 +15,6 @@ import scorex.core.network.peer.PeerInfo */ case class ConnectedPeer(connectionId: ConnectionId, handlerRef: ActorRef, - var lastMessage: Long, peerInfo: Option[PeerInfo]) { override def hashCode(): Int = connectionId.remoteAddress.hashCode() @@ -45,7 +44,7 @@ object ConnectedPeer { val optionalFields = List( peer.peerInfo.map(_.peerSpec.protocolVersion.toString).map("version" -> _.asJson), - Option(peer.lastMessage).filter(_ != 0L).map("lastMessage" -> _.asJson) + peer.peerInfo.map(_.lastStoredActivityTime).filter(_ != 0L).map("lastMessage" -> _.asJson), ).flatten val fields = addressField :: optionalFields Json.obj(fields:_*) diff --git a/src/main/scala/scorex/core/network/NetworkController.scala b/src/main/scala/scorex/core/network/NetworkController.scala index 6eda4d3150..bee88fb2a1 100644 --- a/src/main/scala/scorex/core/network/NetworkController.scala +++ b/src/main/scala/scorex/core/network/NetworkController.scala @@ -116,25 +116,24 @@ class NetworkController(ergoSettings: ErgoSettings, private def time(): Time = System.currentTimeMillis() private def businessLogic: Receive = { - //a message coming in from another peer + // a message coming in from another peer case msg@Message(spec, _, Some(remote)) => messageHandlers.get(spec.messageCode) match { case Some(handler) => handler ! msg // forward the message to the appropriate handler for processing case None => log.error(s"No handlers found for message $remote: " + spec.messageCode) } - // Update last seen message timestamps, global and peer's, with the message timestamp + // Update last seen message timestamps with the message timestamp val remoteAddress = remote.connectionId.remoteAddress connections.get(remoteAddress) match { case Some(cp) => val now = time() lastIncomingMessageTime = now - cp.lastMessage = now - // Update peer's last activity time every X minutes inside PeerInfo - cp.peerInfo.foreach { x => - if ((now - x.lastStoredActivityTime) > activityDelta) { - val peerInfo = x.copy(lastStoredActivityTime = now) - peerManagerRef ! AddOrUpdatePeer(peerInfo) + // Update peer's last activity time every ${activityDelta} minutes inside PeerInfo + cp.peerInfo.foreach { peerInfo => + if ((now - peerInfo.lastStoredActivityTime) > activityDelta) { + val peerInfoUpdated = peerInfo.copy(lastStoredActivityTime = now) + peerManagerRef ! AddOrUpdatePeer(peerInfoUpdated) } } @@ -297,19 +296,34 @@ class NetworkController(ergoSettings: ErgoSettings, () => { // Drop connections with peers if they seem to be inactive val now = time() - connections.values.foreach { cp => - val lastSeen = cp.lastMessage - val timeout = networkSettings.inactiveConnectionDeadline.toMillis - val delta = now - lastSeen - if (delta > timeout) { - log.info(s"Dropping connection with ${cp.peerInfo}, last seen ${delta / 1000.0} seconds ago") - cp.handlerRef ! CloseConnection + connections.values.foreach { cp => + cp.peerInfo.foreach { peerInfo => + val lastSeen = peerInfo.lastStoredActivityTime + val timeout = networkSettings.inactiveConnectionDeadline.toMillis + val delta = now - lastSeen + if (delta > timeout) { + log.info(s"Dropping connection with ${peerInfo}, last seen ${delta / 1000.0} seconds ago") + cp.handlerRef ! CloseConnection + } } } } } } + /** + * Check if a given IPv4 or IPv6 address is local. + * @param remote - address to check + * @return true if the address is local, false otherwise + */ + private def checkLocalOnly(remote: InetSocketAddress): Boolean = + if(!networkSettings.localOnly) { // not only accept local + val address = remote.getAddress + address.isSiteLocalAddress || address.isLinkLocalAddress + } else { + false + } + /** * Connect to peer * @@ -320,13 +334,17 @@ class NetworkController(ergoSettings: ErgoSettings, getPeerAddress(peer) match { case Some(remote) => if (connectionForPeerAddress(remote).isEmpty && !unconfirmedConnections.contains(remote)) { - unconfirmedConnections += remote - tcpManager ! Connect( - remoteAddress = remote, - options = Nil, - timeout = Some(networkSettings.connectionTimeout), - pullMode = true - ) + if (checkLocalOnly(remote)) { + log.warn(s"Prevented attempt to connect to local peer $remote. (scorex.network.localOnly is false)") + } else { + unconfirmedConnections += remote + tcpManager ! Connect( + remoteAddress = remote, + options = Nil, + timeout = Some(networkSettings.connectionTimeout), + pullMode = true + ) + } } else { log.warn(s"Connection to peer $remote is already established") } @@ -383,7 +401,7 @@ class NetworkController(ergoSettings: ErgoSettings, val handler = context.actorOf(handlerProps) // launch connection handler context.watch(handler) - val connectedPeer = ConnectedPeer(connectionId, handler, time(), None) + val connectedPeer = ConnectedPeer(connectionId, handler, None) connections += connectionId.remoteAddress -> connectedPeer unconfirmedConnections -= connectionId.remoteAddress } diff --git a/src/main/scala/scorex/core/network/PeerConnectionHandler.scala b/src/main/scala/scorex/core/network/PeerConnectionHandler.scala index 80cbbce51b..84bb3ac2f4 100644 --- a/src/main/scala/scorex/core/network/PeerConnectionHandler.scala +++ b/src/main/scala/scorex/core/network/PeerConnectionHandler.scala @@ -72,7 +72,7 @@ class PeerConnectionHandler(scorexSettings: ScorexSettings, log.info(s"Got a Handshake from $connectionId") val peerInfo = PeerInfo(receivedHandshake.peerSpec, System.currentTimeMillis(), Some(direction)) - val peer = ConnectedPeer(connectionDescription.connectionId, self, 0, Some(peerInfo)) + val peer = ConnectedPeer(connectionDescription.connectionId, self, Some(peerInfo)) selfPeer = Some(peer) networkControllerRef ! Handshaked(peerInfo) diff --git a/src/main/scala/scorex/core/transaction/TooHighCostError.scala b/src/main/scala/scorex/core/transaction/TooHighCostError.scala new file mode 100644 index 0000000000..e86d3d6189 --- /dev/null +++ b/src/main/scala/scorex/core/transaction/TooHighCostError.scala @@ -0,0 +1,9 @@ +package scorex.core.transaction + +import org.ergoplatform.modifiers.mempool.ErgoTransaction + +/** + * Exception which is indicating that transaction had too high cost during validation + */ +case class TooHighCostError(tx: ErgoTransaction, txCost: Option[Int]) + extends Exception(s"Transaction $tx has too high cost ${txCost.map(_.toString).getOrElse("")}") diff --git a/src/main/scala/scorex/core/transaction/state/StateFeature.scala b/src/main/scala/scorex/core/transaction/state/StateFeature.scala deleted file mode 100644 index ebccd8a23f..0000000000 --- a/src/main/scala/scorex/core/transaction/state/StateFeature.scala +++ /dev/null @@ -1,21 +0,0 @@ -package scorex.core.transaction.state - -import org.ergoplatform.modifiers.mempool.ErgoTransaction -import scala.util.Try - -/** - * Basic trait for features supported by state representation - */ -trait StateFeature - -/** - * Instance of this trait supports stateful validation of any transaction - */ -trait TransactionValidation extends StateFeature { - def validateWithCost(tx: ErgoTransaction, maxTxCost: Int): Try[Int] -} - -object TransactionValidation { - case class TooHighCostError(message: String) extends Exception(message) -} - diff --git a/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala b/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala index c13bd6cb0c..1e7860907f 100644 --- a/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala +++ b/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala @@ -7,7 +7,7 @@ import akka.http.scaladsl.testkit.ScalatestRouteTest import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport import io.circe.Json import io.circe.syntax._ -import org.ergoplatform.ErgoBox.{NonMandatoryRegisterId, TokenId} +import org.ergoplatform.ErgoBox.{AdditionalRegisters, NonMandatoryRegisterId, TokenId} import org.ergoplatform.http.api.{ApiCodecs, TransactionsApiRoute} import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} import org.ergoplatform.nodeView.ErgoReadersHolder.{GetDataFromHistory, GetReaders, Readers} @@ -22,7 +22,7 @@ import sigmastate.SType import sigmastate.Values.{ByteArrayConstant, EvaluatedValue} import sigmastate.eval.Extensions._ import sigmastate.eval._ -import special.collection.Extensions._ +import sigma.Extensions._ import java.net.InetSocketAddress import scala.concurrent.duration._ @@ -243,7 +243,7 @@ class TransactionApiRouteSpec extends AnyFlatSpec Map( ErgoBox.R4 -> ByteArrayConstant("name".getBytes("UTF-8")), ErgoBox.R5 -> ByteArrayConstant("4".getBytes("UTF-8")), - ).asJson + ).asInstanceOf[AdditionalRegisters].asJson Post(prefix + s"/unconfirmed/outputs/byRegisters", searchedRegs) ~> chainedRoute ~> check { status shouldBe StatusCodes.OK @@ -271,7 +271,7 @@ class TransactionApiRouteSpec extends AnyFlatSpec ErgoBox.R4 -> ByteArrayConstant("name".getBytes("UTF-8")), ErgoBox.R5 -> ByteArrayConstant("4".getBytes("UTF-8")), ErgoBox.R6 -> ByteArrayConstant("description".getBytes("UTF-8")), - ).asJson + ).asInstanceOf[AdditionalRegisters].asJson Post(prefix + s"/unconfirmed/outputs/byRegisters", searchedRegs) ~> chainedRoute ~> check { status shouldBe StatusCodes.OK diff --git a/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala b/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala index 5096dec09d..f018569c5e 100644 --- a/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala +++ b/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala @@ -54,7 +54,7 @@ class MempoolAuditorSpec extends AnyFlatSpec with NodeViewTestOps with ErgoTestH val boxes = ErgoState.newBoxes(genesis.transactions).find(_.ergoTree == Constants.TrueLeaf) boxes.nonEmpty shouldBe true - val script = s"{sigmaProp(HEIGHT == ${genesis.height})}" + val script = s"{sigmaProp(HEIGHT == ${genesis.height} + 1)}" val compiler = new SigmaCompiler(ErgoAddressEncoder.MainnetNetworkPrefix) val prop = compiler.compile(emptyEnv, script).buildTree val tree = ErgoTree.fromProposition(prop.asSigmaProp) diff --git a/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala b/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala index 66aa6a8c05..1c666e18e4 100644 --- a/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala +++ b/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala @@ -7,7 +7,7 @@ import org.ergoplatform.settings.MonetarySettings import org.ergoplatform.utils.{ErgoPropertyTest, RandomWrapper} import org.ergoplatform.wallet.interpreter.ErgoInterpreter import org.scalacheck.Gen -import sigmastate.basics.DLogProtocol.ProveDlog +import sigmastate.crypto.DLogProtocol.ProveDlog import scala.concurrent.duration._ @@ -167,7 +167,7 @@ class CandidateGeneratorPropSpec extends ErgoPropertyTest { val newBoxes = fromBigMempool.flatMap(_.outputs) val costs: Seq[Int] = fromBigMempool.map { tx => - us.validateWithCost(tx, Some(upcomingContext), Int.MaxValue, Some(verifier)).getOrElse { + us.validateWithCost(tx, upcomingContext, Int.MaxValue, Some(verifier)).getOrElse { val boxesToSpend = tx.inputs.map(i => newBoxes.find(b => b.id sameElements i.boxId).get) tx.statefulValidity(boxesToSpend, IndexedSeq(), upcomingContext).get diff --git a/src/test/scala/org/ergoplatform/mining/CandidateGeneratorSpec.scala b/src/test/scala/org/ergoplatform/mining/CandidateGeneratorSpec.scala index 0fddaf5750..148d5ebffb 100644 --- a/src/test/scala/org/ergoplatform/mining/CandidateGeneratorSpec.scala +++ b/src/test/scala/org/ergoplatform/mining/CandidateGeneratorSpec.scala @@ -19,8 +19,8 @@ import org.ergoplatform.utils.ErgoTestHelpers import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, ErgoTreePredef, Input} import org.scalatest.concurrent.Eventually import org.scalatest.flatspec.AnyFlatSpec -import sigmastate.basics.DLogProtocol -import sigmastate.basics.DLogProtocol.DLogProverInput +import sigmastate.crypto.DLogProtocol +import sigmastate.crypto.DLogProtocol.DLogProverInput import scala.concurrent.duration._ diff --git a/src/test/scala/org/ergoplatform/mining/ErgoMinerSpec.scala b/src/test/scala/org/ergoplatform/mining/ErgoMinerSpec.scala index 16e83f5d00..25258277e3 100644 --- a/src/test/scala/org/ergoplatform/mining/ErgoMinerSpec.scala +++ b/src/test/scala/org/ergoplatform/mining/ErgoMinerSpec.scala @@ -26,8 +26,8 @@ import org.scalatest.concurrent.Eventually import org.scalatest.flatspec.AnyFlatSpec import sigmastate.SigmaAnd import sigmastate.Values.{ErgoTree, SigmaPropConstant} -import sigmastate.basics.DLogProtocol -import sigmastate.basics.DLogProtocol.DLogProverInput +import sigmastate.crypto.DLogProtocol +import sigmastate.crypto.DLogProtocol.DLogProverInput import scala.annotation.tailrec import scala.concurrent.ExecutionContext.Implicits.global @@ -114,7 +114,7 @@ class ErgoMinerSpec extends AnyFlatSpec with ErgoTestHelpers with ValidBlocksGen val txCost = state.validateWithCost( ErgoTransaction(costlyTx.inputs, costlyTx.dataInputs, costlyTx.outputCandidates), - Some(r.s.stateContext), + r.s.stateContext, costLimit = 440000, None ).get diff --git a/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala b/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala index ee110f90fc..4901e7526b 100644 --- a/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala +++ b/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala @@ -14,19 +14,21 @@ import org.ergoplatform.wallet.interpreter.{ErgoInterpreter, TransactionHintsBag import org.ergoplatform.wallet.protocol.context.InputContext import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, Input} import org.scalacheck.Gen -import scalan.util.BenchmarkUtil +import sigma.util.BenchmarkUtil import scorex.crypto.authds.ADKey import scorex.crypto.hash.Blake2b256 import scorex.util.encode.Base16 import scorex.util.{ModifierId, bytesToId} +import sigma.Colls import sigmastate.AND import sigmastate.Values.{ByteArrayConstant, ByteConstant, IntConstant, LongArrayConstant, SigmaPropConstant, TrueLeaf} -import sigmastate.basics.CryptoConstants -import sigmastate.basics.DLogProtocol.ProveDlog +import sigmastate.crypto.CryptoConstants +import sigmastate.crypto.DLogProtocol.ProveDlog import sigmastate.eval._ import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.{ContextExtension, ProverResult} import sigmastate.eval.Extensions._ + import scala.util.{Random, Try} class ErgoTransactionSpec extends ErgoPropertyTest with ErgoTestConstants { diff --git a/src/test/scala/org/ergoplatform/modifiers/mempool/ExpirationSpecification.scala b/src/test/scala/org/ergoplatform/modifiers/mempool/ExpirationSpecification.scala index ae85bade3b..7398a9ce5f 100644 --- a/src/test/scala/org/ergoplatform/modifiers/mempool/ExpirationSpecification.scala +++ b/src/test/scala/org/ergoplatform/modifiers/mempool/ExpirationSpecification.scala @@ -6,6 +6,7 @@ import org.ergoplatform.utils.ErgoPropertyTest import org.ergoplatform.wallet.interpreter.ErgoInterpreter import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, Input} import org.scalatest.Assertion +import sigma.Colls import sigmastate.Values.ShortConstant import sigmastate.interpreter.{ContextExtension, ProverResult} import sigmastate.eval._ diff --git a/src/test/scala/org/ergoplatform/network/ErgoNodeViewSynchronizerSpecification.scala b/src/test/scala/org/ergoplatform/network/ErgoNodeViewSynchronizerSpecification.scala index 6c5055ed5e..2430aa7728 100644 --- a/src/test/scala/org/ergoplatform/network/ErgoNodeViewSynchronizerSpecification.scala +++ b/src/test/scala/org/ergoplatform/network/ErgoNodeViewSynchronizerSpecification.scala @@ -157,7 +157,6 @@ class ErgoNodeViewSynchronizerSpecification extends HistoryTestHelpers with Matc val p: ConnectedPeer = ConnectedPeer( connectionIdGen.sample.get, pchProbe.ref, - lastMessage = 0, Some(peerInfo) ) synchronizerMockRef ! ChangedHistory(history) @@ -197,7 +196,6 @@ class ErgoNodeViewSynchronizerSpecification extends HistoryTestHelpers with Matc val peer: ConnectedPeer = ConnectedPeer( connectionIdGen.sample.get, pchProbe.ref, - lastMessage = 0, Some(peerInfo) ) } diff --git a/src/test/scala/org/ergoplatform/network/ErgoSyncTrackerSpecification.scala b/src/test/scala/org/ergoplatform/network/ErgoSyncTrackerSpecification.scala index 0f9580ce63..b96081117e 100644 --- a/src/test/scala/org/ergoplatform/network/ErgoSyncTrackerSpecification.scala +++ b/src/test/scala/org/ergoplatform/network/ErgoSyncTrackerSpecification.scala @@ -8,9 +8,9 @@ import scorex.core.network.peer.PeerInfo class ErgoSyncTrackerSpecification extends ErgoPropertyTest { property("getters test") { val time = 10L - val peerInfo = PeerInfo(defaultPeerSpec, time, Some(Incoming)) + val peerInfo = PeerInfo(defaultPeerSpec, time, Some(Incoming), 5L) val cid = ConnectionId(inetAddr1, inetAddr2, Incoming) - val connectedPeer = ConnectedPeer(cid, handlerRef = null, lastMessage = 5L, Some(peerInfo)) + val connectedPeer = ConnectedPeer(cid, handlerRef = null, Some(peerInfo)) val syncTracker = ErgoSyncTracker(settings.scorexSettings.network) val height = 1000 diff --git a/src/test/scala/org/ergoplatform/network/HeaderSerializationSpecification.scala b/src/test/scala/org/ergoplatform/network/HeaderSerializationSpecification.scala index 1196482d4c..8270804c48 100644 --- a/src/test/scala/org/ergoplatform/network/HeaderSerializationSpecification.scala +++ b/src/test/scala/org/ergoplatform/network/HeaderSerializationSpecification.scala @@ -9,7 +9,7 @@ import scorex.crypto.authds.ADDigest import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.ModifierId import scorex.util.encode.Base16 -import sigmastate.basics.CryptoConstants.EcPointType +import sigmastate.crypto.CryptoConstants.EcPointType import java.nio.ByteBuffer diff --git a/src/test/scala/org/ergoplatform/network/PeerFilteringRuleSpecification.scala b/src/test/scala/org/ergoplatform/network/PeerFilteringRuleSpecification.scala index 5ff3499832..cd7ee1e71e 100644 --- a/src/test/scala/org/ergoplatform/network/PeerFilteringRuleSpecification.scala +++ b/src/test/scala/org/ergoplatform/network/PeerFilteringRuleSpecification.scala @@ -12,8 +12,8 @@ class PeerFilteringRuleSpecification extends ErgoPropertyTest { private def peerWithVersion(version: Version): ConnectedPeer = { val ref = ActorRef.noSender val peerSpec = PeerSpec("", version, "", None, Seq.empty) - val peerInfo = PeerInfo(peerSpec, lastHandshake = 0L, None) - ConnectedPeer(ConnectionId(null, null, null), ref, lastMessage = 0L, Some(peerInfo)) + val peerInfo = PeerInfo(peerSpec, lastHandshake = 0L, None, 0L) + ConnectedPeer(ConnectionId(null, null, null), ref, Some(peerInfo)) } property("syncv2 filter") { @@ -25,25 +25,6 @@ class PeerFilteringRuleSpecification extends ErgoPropertyTest { SyncV2Filter.filter(Seq(v1Peer, v2Peer0, v2Peer1, v2Peer2)) shouldBe Seq(v2Peer0, v2Peer1, v2Peer2) } - property("digest mode filter") { - val beforePeer = peerWithVersion(Version(4, 0, 21)) - val afterPeer0 = peerWithVersion(Version(4, 0, 22)) - val afterPeer1 = peerWithVersion(Version(5, 0, 0)) - - DigestModeFilter.filter(Seq(beforePeer, afterPeer0, afterPeer1)) shouldBe Seq(afterPeer0, afterPeer1) - } - - property("broken modifiers filter") { - val inPeer0 = peerWithVersion(Version(4, 0, 17)) - val inPeer1 = peerWithVersion(Version(4, 0, 18)) - val outPeer0 = peerWithVersion(Version(4, 0, 16)) - val outPeer1 = peerWithVersion(Version(4, 0, 19)) - val outPeer2 = peerWithVersion(Version(5, 0, 0)) - - BrokenModifiersFilter.filter(Seq(inPeer0, inPeer1, outPeer0, outPeer1, outPeer2)) shouldBe - Seq(outPeer0, outPeer1, outPeer2) - } - property("utxo set snapshot filter") { val peer0 = peerWithVersion(Version(4, 0, 17)) val peer1 = peerWithVersion(Version(4, 0, 18)) diff --git a/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala index af1e38d5aa..7fd4541e53 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala @@ -18,10 +18,10 @@ import org.ergoplatform.settings.{ErgoSettings, NetworkType, NipopowSettings, No import org.ergoplatform.utils.{ErgoPropertyTest, ErgoTestHelpers, HistoryTestHelpers} import scorex.util.{ModifierId, bytesToId} import sigmastate.Values -import sigmastate.basics.DLogProtocol.ProveDlog +import sigmastate.crypto.DLogProtocol.ProveDlog import sigmastate.eval.Extensions._ import sigmastate.eval._ -import special.collection.Coll +import sigma.{Coll, Colls} import spire.implicits.cfor import java.io.File @@ -38,7 +38,7 @@ class ExtraIndexerSpecification extends ErgoPropertyTest with ExtraIndexerBase w override protected implicit val addressEncoder: ErgoAddressEncoder = initSettings.chainSettings.addressEncoder val nodeSettings: NodeConfigurationSettings = NodeConfigurationSettings(StateType.Utxo, verifyTransactions = true, - -1, UtxoSettings(false, 0, 2), NipopowSettings(false, 1, ChainGenerator.minimalSuffix), mining = false, ChainGenerator.txCostLimit, ChainGenerator.txSizeLimit, useExternalMiner = false, + -1, UtxoSettings(false, 0, 2), NipopowSettings(false, 1), mining = false, ChainGenerator.txCostLimit, ChainGenerator.txSizeLimit, useExternalMiner = false, internalMinersCount = 1, internalMinerPollingInterval = 1.second, miningPubKeyHex = None, offlineGeneration = false, 200, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, rebroadcastCount = 20, 1000000, 100, adProofsSuffixLength = 112 * 1024, extraIndex = false) diff --git a/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala b/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala index d08408e7cf..38aaa799d3 100644 --- a/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala @@ -72,7 +72,8 @@ class ErgoMemPoolSpec extends AnyFlatSpec var poolCost = ErgoMemPool.empty(sortByCostSettings) poolCost = poolCost.process(UnconfirmedTransaction(tx, None), wus)._1 - val cost = wus.validateWithCost(tx, Int.MaxValue).get + val validationContext = wus.stateContext.simplifiedUpcoming() + val cost = wus.validateWithCost(tx, validationContext, Int.MaxValue, None).get poolCost.pool.orderedTransactions.firstKey.weight shouldBe OrderedTxPool.weighted(tx, cost).weight } diff --git a/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala b/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala index ce643b96ef..7f2f6b7cca 100644 --- a/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala @@ -9,8 +9,8 @@ import org.ergoplatform.{ErgoBox, ErgoTreePredef, Height, Self} import scorex.crypto.authds.avltree.batch.Remove import sigmastate.Values._ import sigmastate._ -import sigmastate.basics.CryptoConstants.dlogGroup -import sigmastate.basics.DLogProtocol.ProveDlog +import sigmastate.crypto.CryptoConstants.dlogGroup +import sigmastate.crypto.DLogProtocol.ProveDlog import sigmastate.eval.{CompiletimeIRContext, IRContext} import sigmastate.lang.Terms._ import sigmastate.lang.{CompilerSettings, SigmaCompiler, TransformingSigmaBuilder} diff --git a/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateSpecification.scala index a0cd911b10..e3dc146a8c 100644 --- a/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateSpecification.scala @@ -15,13 +15,13 @@ import org.ergoplatform.settings.Constants import org.ergoplatform.utils.{ErgoPropertyTest, RandomWrapper} import org.ergoplatform.utils.generators.ErgoTransactionGenerators import scorex.core._ -import scorex.core.transaction.state.TransactionValidation.TooHighCostError +import scorex.core.transaction.TooHighCostError import scorex.crypto.authds.ADKey import scorex.db.ByteArrayWrapper import scorex.util.{ModifierId, bytesToId} import scorex.util.encode.Base16 import sigmastate.Values.ByteArrayConstant -import sigmastate.basics.DLogProtocol.{DLogProverInput, ProveDlog} +import sigmastate.crypto.DLogProtocol.{DLogProverInput, ProveDlog} import sigmastate.interpreter.ProverResult import sigmastate.helpers.TestingHelpers._ @@ -51,7 +51,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera val unsignedTx = new UnsignedErgoTransaction(inputs, IndexedSeq(), newBoxes) val tx: ErgoTransaction = ErgoTransaction(defaultProver.sign(unsignedTx, IndexedSeq(foundersBox), emptyDataBoxes, us.stateContext).get) val txCostLimit = initSettings.nodeSettings.maxTransactionCost - us.validateWithCost(tx, None, txCostLimit, None).get should be <= 100000 + us.validateWithCost(tx, us.stateContext.simplifiedUpcoming(), txCostLimit, None).get should be <= 100000 val block1 = validFullBlock(Some(lastBlock), us, Seq(ErgoTransaction(tx))) us = us.applyModifier(block1, None)(_ => ()).get foundersBox = tx.outputs.head @@ -94,17 +94,18 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera ) val unsignedTx = new UnsignedErgoTransaction(inputs, IndexedSeq(), newBoxes) val tx = ErgoTransaction(defaultProver.sign(unsignedTx, IndexedSeq(foundersBox), emptyDataBoxes, us.stateContext).get) - val validationRes1 = us.validateWithCost(tx, 100000) + val validationContext = us.stateContext.simplifiedUpcoming() + val validationRes1 = us.validateWithCost(tx, validationContext, 100000, None) validationRes1 shouldBe 'success val txCost = validationRes1.get - val validationRes2 = us.validateWithCost(tx, txCost - 1) + val validationRes2 = us.validateWithCost(tx, validationContext, txCost - 1, None) validationRes2 shouldBe 'failure validationRes2.toEither.left.get.isInstanceOf[TooHighCostError] shouldBe true - us.validateWithCost(tx, txCost + 1) shouldBe 'success + us.validateWithCost(tx, validationContext, txCost + 1, None) shouldBe 'success - us.validateWithCost(tx, txCost) shouldBe 'success + us.validateWithCost(tx, validationContext, txCost, None) shouldBe 'success height = height + 1 } diff --git a/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSpec.scala b/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSpec.scala index 9659837cb8..5f5967d4d3 100644 --- a/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSpec.scala @@ -17,7 +17,7 @@ import org.scalacheck.Gen import org.scalatest.concurrent.Eventually import scorex.util.ModifierId import scorex.util.encode.Base16 -import sigmastate.basics.DLogProtocol.DLogProverInput +import sigmastate.crypto.DLogProtocol.DLogProverInput import sigmastate.eval.Extensions._ import sigmastate.eval._ import sigmastate.{CAND, CTHRESHOLD} diff --git a/src/test/scala/org/ergoplatform/reemission/ReemissionRulesSpec.scala b/src/test/scala/org/ergoplatform/reemission/ReemissionRulesSpec.scala index 2d5eca8bf7..46a670cab6 100644 --- a/src/test/scala/org/ergoplatform/reemission/ReemissionRulesSpec.scala +++ b/src/test/scala/org/ergoplatform/reemission/ReemissionRulesSpec.scala @@ -5,9 +5,10 @@ import org.ergoplatform.settings.{MonetarySettings, ReemissionSettings} import org.ergoplatform.utils.{ErgoPropertyTest, ErgoTestConstants} import scorex.crypto.hash.Blake2b256 import scorex.util.ModifierId +import sigma.Colls import sigmastate.AvlTreeData import sigmastate.TrivialProp.TrueProp -import sigmastate.eval.{Colls, Digest32Coll} +import sigmastate.eval.Digest32Coll import sigmastate.helpers.TestingHelpers.testBox import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.interpreter.Interpreter.emptyEnv diff --git a/src/test/scala/org/ergoplatform/sanity/ErgoSanityDigest.scala b/src/test/scala/org/ergoplatform/sanity/ErgoSanityDigest.scala index 674f851227..d9b07a50e7 100644 --- a/src/test/scala/org/ergoplatform/sanity/ErgoSanityDigest.scala +++ b/src/test/scala/org/ergoplatform/sanity/ErgoSanityDigest.scala @@ -84,13 +84,12 @@ class ErgoSanityDigest extends ErgoSanity[DIGEST_ST] { val tx = validErgoTransactionGenTemplate(minAssets = 0, maxAssets = 0).sample.get._2 - val peerInfo = PeerInfo(defaultPeerSpec, Long.MaxValue) + val peerInfo = PeerInfo(defaultPeerSpec, Long.MaxValue, None, 0L) @SuppressWarnings(Array("org.wartremover.warts.OptionPartial")) val p: ConnectedPeer = ConnectedPeer( connectionIdGen.sample.get, pchProbe.ref, - lastMessage = 0, Some(peerInfo) ) ref ! ChangedHistory(h) diff --git a/src/test/scala/org/ergoplatform/sanity/ErgoSanityUTXO.scala b/src/test/scala/org/ergoplatform/sanity/ErgoSanityUTXO.scala index 52cb2fbaab..ef6107e35b 100644 --- a/src/test/scala/org/ergoplatform/sanity/ErgoSanityUTXO.scala +++ b/src/test/scala/org/ergoplatform/sanity/ErgoSanityUTXO.scala @@ -78,12 +78,11 @@ class ErgoSanityUTXO extends ErgoSanity[UTXO_ST] with ErgoTestHelpers { val tx = validErgoTransactionGenTemplate(minAssets = 0, maxAssets = 0).sample.get._2 - val peerInfo = PeerInfo(defaultPeerSpec, System.currentTimeMillis()) + val peerInfo = PeerInfo(defaultPeerSpec, System.currentTimeMillis(), None, 0L) @SuppressWarnings(Array("org.wartremover.warts.OptionPartial")) val p: ConnectedPeer = ConnectedPeer( connectionIdGen.sample.get, pchProbe.ref, - lastMessage = 0, Some(peerInfo) ) ref ! ChangedHistory(h) diff --git a/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala b/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala index 571586ca26..016696d708 100644 --- a/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala +++ b/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala @@ -3,7 +3,7 @@ package org.ergoplatform.serialization import io.circe.syntax._ import io.circe.{ACursor, Decoder, Encoder, Json} import org.ergoplatform.ErgoBox -import org.ergoplatform.ErgoBox.NonMandatoryRegisterId +import org.ergoplatform.ErgoBox.{AdditionalRegisters, NonMandatoryRegisterId} import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.http.api.ApiEncoderOption.HideDetails.implicitValue import org.ergoplatform.http.api.ApiEncoderOption.{Detalization, ShowDetails} @@ -187,7 +187,7 @@ class JsonSerializationSpec extends ErgoPropertyTest with WalletGenerators with stringify(decodedAssets) should contain theSameElementsAs stringify(assets) } - private def checkRegisters(c: ACursor, registers: Map[NonMandatoryRegisterId, _ <: EvaluatedValue[_ <: SType]]) = { + private def checkRegisters(c: ACursor, registers: AdditionalRegisters) = { val Right(decodedRegs) = c.as[Map[NonMandatoryRegisterId, EvaluatedValue[SType]]] decodedRegs should contain theSameElementsAs registers } diff --git a/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala b/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala index 4da85bf73d..e222ec2d88 100644 --- a/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala +++ b/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala @@ -25,7 +25,7 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { verifyTransactions = true, 1000, utxoSettings = UtxoSettings(false, 0, 2), - nipopowSettings = NipopowSettings(false, 1, 10), + nipopowSettings = NipopowSettings(false, 1), mining = true, txCostLimit, txSizeLimit, @@ -74,7 +74,7 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { verifyTransactions = true, 12, utxoSettings = UtxoSettings(false, 0, 2), - nipopowSettings = NipopowSettings(false, 1, 10), + nipopowSettings = NipopowSettings(false, 1), mining = true, txCostLimit, txSizeLimit, @@ -116,7 +116,7 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { verifyTransactions = true, 13, utxoSettings = UtxoSettings(false, 0, 2), - nipopowSettings = NipopowSettings(false, 1, 10), + nipopowSettings = NipopowSettings(false, 1), mining = true, txCostLimit, txSizeLimit, diff --git a/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala b/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala index 19233883be..7fb07b62f9 100644 --- a/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala +++ b/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala @@ -16,7 +16,7 @@ import org.ergoplatform.settings._ import org.ergoplatform.utils.{ErgoTestHelpers, HistoryTestHelpers} import org.ergoplatform.wallet.boxes.{BoxSelector, ReplaceCompactCollectBoxSelector} import scorex.util.ModifierId -import sigmastate.basics.DLogProtocol.ProveDlog +import sigmastate.crypto.DLogProtocol.ProveDlog import java.io.File import scala.annotation.tailrec @@ -59,7 +59,7 @@ object ChainGenerator extends App with ErgoTestHelpers { val txCostLimit = initSettings.nodeSettings.maxTransactionCost val txSizeLimit = initSettings.nodeSettings.maxTransactionSize val nodeSettings: NodeConfigurationSettings = NodeConfigurationSettings(StateType.Utxo, verifyTransactions = true, - -1, UtxoSettings(false, 0, 2), NipopowSettings(false, 1, minimalSuffix), mining = false, txCostLimit, txSizeLimit, useExternalMiner = false, + -1, UtxoSettings(false, 0, 2), NipopowSettings(false, 1), mining = false, txCostLimit, txSizeLimit, useExternalMiner = false, internalMinersCount = 1, internalMinerPollingInterval = 1.second, miningPubKeyHex = None, offlineGeneration = false, 200, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, rebroadcastCount = 20, 1000000, 100, adProofsSuffixLength = 112*1024, extraIndex = false) diff --git a/src/test/scala/org/ergoplatform/tools/FeeSimulator.scala b/src/test/scala/org/ergoplatform/tools/FeeSimulator.scala index c09e9c6bd9..9eebe3f4b7 100644 --- a/src/test/scala/org/ergoplatform/tools/FeeSimulator.scala +++ b/src/test/scala/org/ergoplatform/tools/FeeSimulator.scala @@ -6,7 +6,8 @@ import org.ergoplatform.settings.LaunchParameters._ import org.ergoplatform.{ErgoBoxCandidate, Input} import scorex.crypto.authds.ADKey import scorex.utils.Random -import sigmastate.basics.DLogProtocol.DLogProverInput +import sigma.Colls +import sigmastate.crypto.DLogProtocol.DLogProverInput import sigmastate.eval.Extensions.ArrayByteOps import sigmastate.eval._ import sigmastate.interpreter.{ContextExtension, ProverResult} diff --git a/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala b/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala index cd24b2d52e..51d7e80597 100644 --- a/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala +++ b/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala @@ -22,8 +22,8 @@ import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Digest32 import scorex.util.ScorexLogging import sigmastate.Values.ErgoTree -import sigmastate.basics.CryptoConstants.EcPointType -import sigmastate.basics.DLogProtocol.{DLogProverInput, ProveDlog} +import sigmastate.crypto.CryptoConstants.EcPointType +import sigmastate.crypto.DLogProtocol.{DLogProverInput, ProveDlog} import sigmastate.interpreter.{ContextExtension, ProverResult} import scala.concurrent.duration._ diff --git a/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala b/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala index 9c51136d1e..b38d920120 100644 --- a/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala +++ b/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala @@ -44,11 +44,10 @@ trait HistoryTestHelpers extends ErgoPropertyTest { initialDiffOpt: Option[BigInt] = None, genesisIdOpt: Option[ModifierId] = None): ErgoHistory = { - val minimalSuffix = 2 val txCostLimit = initSettings.nodeSettings.maxTransactionCost val txSizeLimit = initSettings.nodeSettings.maxTransactionSize val nodeSettings: NodeConfigurationSettings = NodeConfigurationSettings(stateType, verifyTransactions, blocksToKeep, - UtxoSettings(false, 0, 2), NipopowSettings(false, 1, minimalSuffix), mining = false, txCostLimit, txSizeLimit, useExternalMiner = false, + UtxoSettings(false, 0, 2), NipopowSettings(false, 1), mining = false, txCostLimit, txSizeLimit, useExternalMiner = false, internalMinersCount = 1, internalMinerPollingInterval = 1.second, miningPubKeyHex = None, offlineGeneration = false, 200, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, rebroadcastCount = 200, 1000000, 100, adProofsSuffixLength = 112*1024, extraIndex = false diff --git a/src/test/scala/org/ergoplatform/utils/NodeViewTestConfig.scala b/src/test/scala/org/ergoplatform/utils/NodeViewTestConfig.scala index 0e134f3f8c..8df386a697 100644 --- a/src/test/scala/org/ergoplatform/utils/NodeViewTestConfig.scala +++ b/src/test/scala/org/ergoplatform/utils/NodeViewTestConfig.scala @@ -18,7 +18,7 @@ case class NodeViewTestConfig(stateType: StateType, nodeSettings = defaultSettings.nodeSettings.copy( stateType = stateType, verifyTransactions = verifyTransactions, - nipopowSettings = NipopowSettings(popowBootstrap, 1, 10) + nipopowSettings = NipopowSettings(popowBootstrap, 1) ) ) } diff --git a/src/test/scala/org/ergoplatform/utils/Stubs.scala b/src/test/scala/org/ergoplatform/utils/Stubs.scala index 52d008c916..b68886a573 100644 --- a/src/test/scala/org/ergoplatform/utils/Stubs.scala +++ b/src/test/scala/org/ergoplatform/utils/Stubs.scala @@ -42,7 +42,7 @@ import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Digest32 import scorex.db.ByteArrayWrapper import scorex.util.Random -import sigmastate.basics.DLogProtocol.{DLogProverInput, ProveDlog} +import sigmastate.crypto.DLogProtocol.{DLogProverInput, ProveDlog} import scala.collection.mutable import scala.concurrent.duration._ @@ -366,11 +366,10 @@ trait Stubs extends ErgoGenerators with ErgoTestHelpers with ChainGenerator with epochLength: Int = 100000000, useLastEpochs: Int = 10): ErgoHistory = { - val minimalSuffix = 2 val txCostLimit = initSettings.nodeSettings.maxTransactionCost val txSizeLimit = initSettings.nodeSettings.maxTransactionSize val nodeSettings: NodeConfigurationSettings = NodeConfigurationSettings(stateType, verifyTransactions, blocksToKeep, - UtxoSettings(false, 0, 2), NipopowSettings(poPoWBootstrap, 1, minimalSuffix), mining = false, txCostLimit, txSizeLimit, useExternalMiner = false, + UtxoSettings(false, 0, 2), NipopowSettings(poPoWBootstrap, 1), mining = false, txCostLimit, txSizeLimit, useExternalMiner = false, internalMinersCount = 1, internalMinerPollingInterval = 1.second,miningPubKeyHex = None, offlineGeneration = false, 200, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, rebroadcastCount = 200, 1000000, 100, adProofsSuffixLength = 112*1024, extraIndex = false diff --git a/src/test/scala/org/ergoplatform/utils/WalletTestOps.scala b/src/test/scala/org/ergoplatform/utils/WalletTestOps.scala index b76ac7485b..42407311f2 100644 --- a/src/test/scala/org/ergoplatform/utils/WalletTestOps.scala +++ b/src/test/scala/org/ergoplatform/utils/WalletTestOps.scala @@ -16,12 +16,13 @@ import org.ergoplatform.utils.fixtures.WalletFixture import scorex.crypto.authds.ADKey import scorex.crypto.hash.Blake2b256 import scorex.util.ModifierId +import sigma.Colls import sigmastate.Values.ErgoTree -import sigmastate.basics.DLogProtocol.ProveDlog +import sigmastate.crypto.DLogProtocol.ProveDlog import sigmastate.eval.Extensions._ import sigmastate.eval._ import sigmastate.interpreter.ProverResult -import special.collection.Extensions._ +import sigma.Extensions._ trait WalletTestOps extends NodeViewBaseOps { diff --git a/src/test/scala/org/ergoplatform/utils/generators/ChainGenerator.scala b/src/test/scala/org/ergoplatform/utils/generators/ChainGenerator.scala index 441bd039f2..cfe35ca205 100644 --- a/src/test/scala/org/ergoplatform/utils/generators/ChainGenerator.scala +++ b/src/test/scala/org/ergoplatform/utils/generators/ChainGenerator.scala @@ -7,12 +7,13 @@ import org.ergoplatform.modifiers.history.extension.{Extension, ExtensionCandida import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.popow.{NipopowAlgos, PoPowHeader} import org.ergoplatform.modifiers.mempool.ErgoTransaction -import org.ergoplatform.modifiers.{NonHeaderBlockSection, ErgoFullBlock, BlockSection} +import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock, NonHeaderBlockSection} import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.settings.Constants import org.ergoplatform.utils.{BoxUtils, ErgoTestConstants} import scorex.crypto.authds.{ADKey, SerializedAdProof} import scorex.crypto.hash.Digest32 +import sigma.Colls import sigmastate.eval._ import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.{ContextExtension, ProverResult} diff --git a/src/test/scala/org/ergoplatform/utils/generators/ErgoGenerators.scala b/src/test/scala/org/ergoplatform/utils/generators/ErgoGenerators.scala index 9c0eed8a88..ad49b7557a 100644 --- a/src/test/scala/org/ergoplatform/utils/generators/ErgoGenerators.scala +++ b/src/test/scala/org/ergoplatform/utils/generators/ErgoGenerators.scala @@ -23,9 +23,9 @@ import scorex.crypto.authds.{ADDigest, SerializedAdProof} import scorex.crypto.hash.Digest32 import scorex.testkit.generators.CoreGenerators import sigmastate.Values.ErgoTree -import sigmastate.basics.CryptoConstants.EcPointType -import sigmastate.basics.DLogProtocol.{DLogProverInput, ProveDlog} -import sigmastate.basics.{CryptoConstants, DiffieHellmanTupleProverInput, ProveDHTuple} +import sigmastate.crypto.CryptoConstants.EcPointType +import sigmastate.crypto.DLogProtocol.{DLogProverInput, ProveDlog} +import sigmastate.crypto.{CryptoConstants, DiffieHellmanTupleProverInput, ProveDHTuple} import sigmastate.interpreter.ProverResult import scala.util.Random diff --git a/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala b/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala index 7630d2cdbe..978617e43d 100644 --- a/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala +++ b/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala @@ -24,7 +24,7 @@ import scorex.crypto.hash.Blake2b256 import scorex.db.ByteArrayWrapper import scorex.util.encode.Base16 import sigmastate.Values.ErgoTree -import sigmastate.basics.DLogProtocol.ProveDlog +import sigmastate.crypto.DLogProtocol.ProveDlog import sigmastate.eval.Extensions._ import sigmastate.eval._ import sigmastate.helpers.TestingHelpers._ diff --git a/src/test/scala/scorex/testkit/generators/ObjectGenerators.scala b/src/test/scala/scorex/testkit/generators/ObjectGenerators.scala index 7c9dddf961..f99112fab7 100644 --- a/src/test/scala/scorex/testkit/generators/ObjectGenerators.scala +++ b/src/test/scala/scorex/testkit/generators/ObjectGenerators.scala @@ -100,12 +100,12 @@ trait ObjectGenerators { def peerInfoGen: Gen[PeerInfo] = for { peerSpec <- peerSpecGen - } yield PeerInfo(peerSpec, 0L, Some(Incoming)) + } yield PeerInfo(peerSpec, 0L, Some(Incoming), 0L) def connectedPeerGen(peerRef: ActorRef): Gen[ConnectedPeer] = for { connectionId <- connectionIdGen peerInfo <- peerInfoGen - } yield ConnectedPeer(connectionId, peerRef, 0, Some(peerInfo)) + } yield ConnectedPeer(connectionId, peerRef, Some(peerInfo)) def peerSpecGen: Gen[PeerSpec] = for { declaredAddress <- Gen.frequency(5 -> const(None), 5 -> some(inetSocketAddressGen))