├── .dockerignore
├── .editorconfig
├── .github
└── workflows
│ ├── ci.yml
│ ├── docker-publish.yml
│ ├── release-binaries.yml
│ └── release.yml
├── .gitignore
├── .scalafmt.conf
├── .travis.yml
├── Dockerfile
├── FAQ.md
├── LICENSE
├── README.md
├── avldb
├── .gitignore
├── .travis.yml
├── README.md
├── benchmarks
│ └── src
│ │ ├── main
│ │ ├── resources
│ │ │ └── logback-test.xml
│ │ └── scala
│ │ │ └── scorex
│ │ │ └── crypto
│ │ │ └── authds
│ │ │ └── benchmarks
│ │ │ ├── AVLTreeBatchPerformance.scala
│ │ │ └── Helper.scala
│ │ └── test
│ │ └── scala
│ │ ├── CsvReporter.scala
│ │ └── MemoryFootprint.scala
├── build.sbt
├── project
│ ├── build.properties
│ └── plugins.sbt
└── src
│ ├── main
│ ├── java
│ │ └── scorex
│ │ │ └── ByteUtils.java
│ ├── resources
│ │ └── logback.xml
│ └── scala
│ │ ├── org
│ │ └── ergoplatform
│ │ │ └── serialization
│ │ │ ├── ErgoSerializer.scala
│ │ │ ├── ManifestSerializer.scala
│ │ │ └── SubtreeSerializer.scala
│ │ └── scorex
│ │ ├── crypto
│ │ └── authds
│ │ │ └── avltree
│ │ │ └── batch
│ │ │ ├── AvlTreeParameters.scala
│ │ │ ├── Constants.scala
│ │ │ ├── ProverNodeSerializer.scala
│ │ │ ├── ProxyInternalProverNode.scala
│ │ │ └── VersionedLDBAVLStorage.scala
│ │ └── db
│ │ ├── ByteArrayUtils.scala
│ │ ├── ByteArrayWrapper.scala
│ │ ├── KVStoreReader.scala
│ │ ├── LDBFactory.scala
│ │ ├── LDBKVStore.scala
│ │ └── LDBVersionedStore.scala
│ └── test
│ └── scala
│ └── scorex
│ ├── crypto
│ └── authds
│ │ └── avltree
│ │ └── batch
│ │ ├── AVLStorageWithPersistentProverSpec.scala
│ │ ├── LDBVersionedStoreSpecification.scala
│ │ ├── VersionedLDBAVLStorageSpecification.scala
│ │ ├── VersionedLDBAVLStorageStatefulSpecification.scala
│ │ ├── benchmark
│ │ ├── BatchingBenchmark.scala
│ │ ├── LDBVersionedStoreBenchmark.scala
│ │ ├── OOMTest.scala
│ │ ├── OOMTest.scala.orig
│ │ └── OOMTest.scala.rej
│ │ └── helpers
│ │ ├── FileHelper.scala
│ │ └── TestHelper.scala
│ └── db
│ ├── ByteArrayUtilsSpec.scala
│ └── LDBVersionedStoreSpec.scala
├── build.sbt
├── ci
├── ergo.icns
├── import_gpg.sh
└── release-binaries.py
├── do-release.sh
├── ergo-core
├── README.md
├── build.sbt
├── project
│ ├── build.properties
│ └── plugins.sbt
└── src
│ ├── main
│ └── scala
│ │ └── org
│ │ └── ergoplatform
│ │ ├── CriticalSystemException.scala
│ │ ├── NodeViewComponent.scala
│ │ ├── consensus
│ │ ├── ContainsModifiers.scala
│ │ ├── ModifierSemanticValidity.scala
│ │ ├── PeerChainStatus.scala
│ │ ├── ProgressInfo.scala
│ │ └── SyncInfo.scala
│ │ ├── core
│ │ ├── BytesSerializable.scala
│ │ └── core.scala
│ │ ├── http
│ │ └── api
│ │ │ └── ApiCodecs.scala
│ │ ├── local
│ │ ├── NipopowVerificationResult.scala
│ │ └── NipopowVerifier.scala
│ │ ├── mining
│ │ ├── AutolykosPowScheme.scala
│ │ ├── AutolykosSolution.scala
│ │ ├── CandidateBlock.scala
│ │ ├── CandidateUtils.scala
│ │ ├── DefaultFakePowScheme.scala
│ │ ├── ModQHash.scala
│ │ ├── ProofOfUpcomingTransactions.scala
│ │ ├── WorkMessage.scala
│ │ ├── difficulty
│ │ │ ├── DifficultyAdjustment.scala
│ │ │ └── DifficultySerializer.scala
│ │ └── mining.scala
│ │ ├── modifiers
│ │ ├── BlockSection.scala
│ │ ├── ErgoFullBlock.scala
│ │ ├── ErgoNodeViewModifier.scala
│ │ ├── NetworkObjectTypeId.scala
│ │ ├── NonHeaderBlockSection.scala
│ │ ├── TransactionsCarryingBlockSection.scala
│ │ ├── history
│ │ │ ├── ADProofs.scala
│ │ │ ├── BlockTransactions.scala
│ │ │ ├── HeaderChain.scala
│ │ │ ├── HistoryModifierSerializer.scala
│ │ │ ├── PreHeader.scala
│ │ │ ├── extension
│ │ │ │ ├── Extension.scala
│ │ │ │ ├── ExtensionCandidate.scala
│ │ │ │ └── ExtensionSerializer.scala
│ │ │ ├── header
│ │ │ │ ├── Header.scala
│ │ │ │ ├── HeaderSerializer.scala
│ │ │ │ ├── HeaderWithoutPow.scala
│ │ │ │ └── PreGenesisHeader.scala
│ │ │ └── popow
│ │ │ │ ├── NipopowAlgos.scala
│ │ │ │ ├── NipopowProof.scala
│ │ │ │ ├── PoPowHeader.scala
│ │ │ │ └── PoPowParams.scala
│ │ ├── mempool
│ │ │ ├── ErgoTransaction.scala
│ │ │ └── UnsignedErgoTransaction.scala
│ │ ├── state
│ │ │ └── StateChanges.scala
│ │ └── transaction
│ │ │ ├── Signable.scala
│ │ │ └── TooHighCostError.scala
│ │ ├── network
│ │ ├── Handshake.scala
│ │ ├── HandshakeSerializer.scala
│ │ ├── ModePeerFeature.scala
│ │ ├── PeerFeature.scala
│ │ ├── PeerSpec.scala
│ │ ├── Version.scala
│ │ ├── message
│ │ │ ├── GetNipopowProofSpec.scala
│ │ │ ├── InvData.scala
│ │ │ ├── InvSpec.scala
│ │ │ ├── MessageBase.scala
│ │ │ ├── MessageConstants.scala
│ │ │ ├── MessageSpec.scala
│ │ │ ├── ModifiersData.scala
│ │ │ ├── ModifiersSpec.scala
│ │ │ ├── NipopowProofData.scala
│ │ │ ├── NipopowProofSpec.scala
│ │ │ ├── RequestModifierSpec.scala
│ │ │ └── SyncInfoMessageSpec.scala
│ │ └── peer
│ │ │ ├── LocalAddressPeerFeature.scala
│ │ │ ├── RestApiUrlPeerFeature.scala
│ │ │ └── SessionIdPeerFeature.scala
│ │ ├── nodeView
│ │ ├── ErgoContext.scala
│ │ ├── LocallyGeneratedModifier.scala
│ │ ├── history
│ │ │ ├── ErgoHistoryUtils.scala
│ │ │ ├── ErgoSyncInfo.scala
│ │ │ └── storage
│ │ │ │ └── modifierprocessors
│ │ │ │ └── ExtensionValidator.scala
│ │ ├── mempool
│ │ │ └── TransactionMembershipProof.scala
│ │ └── state
│ │ │ ├── ErgoStateContext.scala
│ │ │ ├── StateType.scala
│ │ │ └── VotingData.scala
│ │ ├── reemission
│ │ └── ReemissionRules.scala
│ │ ├── settings
│ │ ├── Algos.scala
│ │ ├── ChainSettings.scala
│ │ ├── ChainSettingsReader.scala
│ │ ├── ClientCapabilities.scala
│ │ ├── Constants.scala
│ │ ├── ErgoValidationSettings.scala
│ │ ├── ErgoValidationSettingsUpdate.scala
│ │ ├── LaunchParameters.scala
│ │ ├── ModifierIdReader.scala
│ │ ├── NipopowSettings.scala
│ │ ├── Parameters.scala
│ │ ├── PeerFeatureDescriptors.scala
│ │ ├── PowSchemeReaders.scala
│ │ ├── ReemissionSettings.scala
│ │ ├── Settings.scala
│ │ ├── SettingsReaders.scala
│ │ ├── UtxoSettings.scala
│ │ ├── ValidationRules.scala
│ │ └── VotingSettings.scala
│ │ ├── utils
│ │ ├── BoxUtils.scala
│ │ ├── LoggingUtil.scala
│ │ ├── ScorexEncoder.scala
│ │ ├── ScorexEncoding.scala
│ │ └── utils.scala
│ │ └── validation
│ │ ├── ModifierError.scala
│ │ ├── ModifierValidator.scala
│ │ ├── ValidationResult.scala
│ │ └── ValidationSettings.scala
│ └── test
│ ├── resources
│ ├── application.conf
│ ├── difficulty.csv
│ ├── execute-script.json
│ ├── logback-test.xml
│ ├── settings.conf
│ └── settings.json
│ └── scala
│ └── org
│ └── ergoplatform
│ ├── examples
│ └── LiteClientExamples.scala
│ ├── mining
│ ├── AutolykosPowSchemeSpec.scala
│ └── difficulty
│ │ ├── DifficultyAdjustmentSpecification.scala
│ │ └── DifficultySerializerSpecification.scala
│ ├── modifiers
│ ├── history
│ │ ├── AdProofSpec.scala
│ │ ├── BlockTransactionsSpec.scala
│ │ ├── ExtensionCandidateTest.scala
│ │ └── PoPowHeaderSpec.scala
│ └── mempool
│ │ └── ErgoTransactionSpec.scala
│ ├── network
│ ├── DecodingUtils.scala
│ ├── HandshakeSpecification.scala
│ ├── HeaderSerializationSpecification.scala
│ ├── ModePeerFeatureSpecification.scala
│ └── PeerSpecSerializerSpec.scala
│ ├── reemission
│ └── ReemissionRulesSpec.scala
│ ├── serialization
│ ├── JsonSerializationCoreSpec.scala
│ └── SerializationCoreTests.scala
│ ├── settings
│ └── VotingSpecification.scala
│ └── utils
│ ├── ErgoCorePropertyTest.scala
│ ├── ErgoCoreTestConstants.scala
│ ├── NoShrink.scala
│ ├── RandomLike.scala
│ ├── SerializationTests.scala
│ └── generators
│ ├── CoreObjectGenerators.scala
│ ├── ErgoCoreGenerators.scala
│ └── ErgoCoreTransactionGenerators.scala
├── ergo-installer.sh
├── ergo-wallet
├── build.sbt
├── project
│ ├── build.properties
│ └── plugins.sbt
└── src
│ ├── main
│ ├── java
│ │ └── org
│ │ │ └── ergoplatform
│ │ │ └── wallet
│ │ │ └── interface4j
│ │ │ ├── SecretString.java
│ │ │ └── crypto
│ │ │ ├── ErgoSignature.java
│ │ │ └── ErgoUnsafeProver.java
│ ├── resources
│ │ └── wordlist
│ │ │ ├── chinese_simplified.txt
│ │ │ ├── chinese_traditional.txt
│ │ │ ├── english.txt
│ │ │ ├── french.txt
│ │ │ ├── italian.txt
│ │ │ ├── japanese.txt
│ │ │ ├── korean.txt
│ │ │ └── spanish.txt
│ └── scala
│ │ └── org
│ │ └── ergoplatform
│ │ ├── contracts
│ │ └── ReemissionContracts.scala
│ │ └── wallet
│ │ ├── Constants.scala
│ │ ├── boxes
│ │ ├── BoxSelector.scala
│ │ ├── DefaultBoxSelector.scala
│ │ ├── ErgoBoxAssetExtractor.scala
│ │ ├── ErgoBoxSerializer.scala
│ │ ├── ReemissionData.scala
│ │ ├── ReplaceCompactCollectBoxSelector.scala
│ │ ├── TrackedBox.scala
│ │ └── TrackedBoxStatus.scala
│ │ ├── crypto
│ │ ├── AES.scala
│ │ ├── ErgoSignature.scala
│ │ └── HmacSHA512.scala
│ │ ├── interpreter
│ │ ├── ErgoInterpreter.scala
│ │ ├── ErgoProvingInterpreter.scala
│ │ ├── ErgoUnsafeProver.scala
│ │ └── TransactionHintsBag.scala
│ │ ├── mnemonic
│ │ ├── Mnemonic.scala
│ │ └── WordList.scala
│ │ ├── protocol
│ │ ├── Constants.scala
│ │ └── context
│ │ │ └── InputContext.scala
│ │ ├── secrets
│ │ ├── EncryptedSecret.scala
│ │ ├── JsonSecretStorage.scala
│ │ └── SecretStorage.scala
│ │ ├── serialization
│ │ ├── ErgoWalletSerializer.scala
│ │ └── JsonCodecsWrapper.scala
│ │ ├── settings
│ │ └── SecretStorageSettings.scala
│ │ ├── transactions
│ │ └── TransactionBuilder.scala
│ │ └── utils
│ │ └── FileUtils.scala
│ └── test
│ ├── java
│ └── org
│ │ └── ergoplatform
│ │ └── wallet
│ │ ├── AddressGenerationDemo.java
│ │ └── CreateTransactionDemo.java
│ └── scala
│ └── org
│ └── ergoplatform
│ └── wallet
│ ├── AssetUtilsSpec.scala
│ ├── boxes
│ ├── DefaultBoxSelectorSpec.scala
│ └── ReplaceCompactCollectBoxSelectorSpec.scala
│ ├── crypto
│ ├── EncryptionSpec.scala
│ └── ErgoSignatureSpec.scala
│ ├── interpreter
│ ├── ErgoProvingInterpreterSpec.scala
│ ├── ErgoUnsafeProverSpec.scala
│ └── InterpreterSpecCommon.scala
│ ├── mnemonic
│ └── MnemonicSpec.scala
│ ├── secrets
│ ├── DerivationPathSpec.scala
│ ├── ExtendedPublicKeySpec.scala
│ ├── ExtendedSecretKeySpec.scala
│ └── JsonSecretStorageSpec.scala
│ ├── serialization
│ └── SerializationSpec.scala
│ ├── transactions
│ └── TransactionBuilderSpec.scala
│ └── utils
│ ├── TestFileUtils.scala
│ ├── WalletGenerators.scala
│ └── WalletTestHelpers.scala
├── findbugs-exclude.xml
├── papers
├── contractual
│ ├── bitcoin.odp
│ ├── bitcoin.png
│ ├── compile.sh
│ ├── llncs.cls
│ ├── main.tex
│ └── sources.bib
├── emission.md
├── ergopool
│ ├── compile.sh
│ ├── llncs.cls
│ ├── main.bib
│ └── main.tex
├── pow_analysis
│ ├── compile.sh
│ ├── main.bib
│ └── main.tex
├── teaser
│ ├── compile.sh
│ ├── emission.jpg
│ ├── references.bib
│ └── teaser.tex
├── utxo.md
├── whitepaper
│ ├── abstract.tex
│ ├── autolykos.tex
│ ├── compile.sh
│ ├── currency.tex
│ ├── img
│ │ ├── batching
│ │ │ ├── proofSizeFromBatchSize2.png
│ │ │ └── proofSizeFromTreeSize.png
│ │ ├── emission.dat
│ │ ├── emission.gnu
│ │ ├── emission.png
│ │ └── proofSize.png
│ ├── intro.tex
│ ├── money.tex
│ ├── references.bib
│ ├── social_contract.tex
│ ├── survivability.tex
│ ├── utxo.tex
│ └── whitepaper.tex
└── yellow
│ ├── YellowPaper.tex
│ ├── block.tex
│ ├── compile.sh
│ ├── economy.tex
│ ├── img
│ ├── box-transition.png
│ ├── box-transition.uxf
│ ├── curve_combined.png
│ └── emission.jpg
│ ├── modifiersProcessing.tex
│ ├── modifiersValidation.tex
│ ├── peerManagement.tex
│ ├── pow
│ ├── ErgoPow.tex
│ ├── compile.sh
│ └── references.bib
│ ├── sync.tex
│ ├── tokens.tex
│ └── voting.tex
├── project
├── build.properties
└── plugins.sbt
├── scalastyle-config.xml
└── src
├── it
├── resources
│ ├── devnetTemplate.conf
│ ├── logback-test.xml
│ ├── mainnetTemplate.conf
│ ├── nodes.conf
│ ├── parameters-template.txt
│ └── testnetTemplate.conf
└── scala
│ └── org
│ └── ergoplatform
│ └── it
│ ├── DeepRollBackSpec.scala
│ ├── ForkResolutionSpec.scala
│ ├── KnownNodesSpec.scala
│ ├── LongChainSyncSpec.scala
│ ├── NodeRecoverySpec.scala
│ ├── OpenApiSpec.scala
│ ├── PrunedDigestNodeSync2Spec.scala
│ ├── PrunedDigestNodeSyncSpec.scala
│ ├── StateRecoveryDigestNodeSpec.scala
│ ├── UtxoStateNodesSyncSpec.scala
│ ├── WalletSpec.scala
│ ├── api
│ ├── NetworkNodeApi.scala
│ ├── NodeApi.scala
│ └── package.scala
│ ├── container
│ ├── ApiChecker.scala
│ ├── Docker.scala
│ ├── IntegrationSuite.scala
│ ├── IntegrationTestConstants.scala
│ ├── Node.scala
│ └── NodeInfo.scala
│ ├── network
│ ├── NetworkClient.scala
│ └── NetworkSender.scala
│ └── util
│ └── util.scala
├── it2
└── scala
│ └── org
│ └── ergoplatform
│ └── it2
│ ├── TestDigestStateOnMainNetSpec.scala
│ ├── TestDigestStateWithPruningOnMainNetSpec.scala
│ └── TestOnMainNetSpec.scala
├── main
├── resources
│ ├── .well-known
│ │ └── ai-plugin.json
│ ├── api
│ │ ├── openapi-ai.yaml
│ │ └── openapi.yaml
│ ├── application.conf
│ ├── devnet.conf
│ ├── logback.xml
│ ├── mainnet.conf
│ ├── node1
│ │ └── application.conf
│ ├── node2
│ │ └── application.conf
│ ├── nodeTestnet
│ │ └── application.conf
│ ├── panel
│ │ ├── asset-manifest.json
│ │ ├── favicon.png
│ │ ├── favicon.svg
│ │ ├── index.html
│ │ ├── manifest.json
│ │ ├── precache-manifest.5ec1b24f55df1ee3e942e6e6777944c3.js
│ │ ├── robots.txt
│ │ ├── service-worker.js
│ │ └── static
│ │ │ ├── css
│ │ │ ├── 2.8e5c4313.chunk.css
│ │ │ ├── 2.8e5c4313.chunk.css.map
│ │ │ ├── main.95603572.chunk.css
│ │ │ └── main.95603572.chunk.css.map
│ │ │ ├── js
│ │ │ ├── 2.82289ad5.chunk.js
│ │ │ ├── 2.82289ad5.chunk.js.map
│ │ │ ├── main.4a62f34b.chunk.js
│ │ │ ├── main.4a62f34b.chunk.js.map
│ │ │ ├── runtime-main.3929add9.js
│ │ │ └── runtime-main.3929add9.js.map
│ │ │ └── media
│ │ │ ├── MaterialIcons-Regular.012cf6a1.woff
│ │ │ ├── MaterialIcons-Regular.570eb838.woff2
│ │ │ ├── MaterialIcons-Regular.a37b0c01.ttf
│ │ │ ├── MaterialIcons-Regular.e79bfd88.eot
│ │ │ ├── Roboto-Bold.ee7b96fa.ttf
│ │ │ ├── Roboto-Light.fc84e998.ttf
│ │ │ ├── Roboto-Medium.d0884059.ttf
│ │ │ ├── Roboto-Regular.3e1af3ef.ttf
│ │ │ ├── Roboto-Thin.89e2666c.ttf
│ │ │ ├── close.feae5a5c.svg
│ │ │ ├── copy.icon.835ebda7.svg
│ │ │ ├── logotype_white.4dcfd639.svg
│ │ │ ├── redo-arrow-symbol.e801de31.svg
│ │ │ └── remove.94c0849a.svg
│ ├── samples
│ │ └── local.conf.sample
│ ├── swagger-ui
│ │ ├── README.md
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ ├── index.html
│ │ ├── oauth2-redirect.html
│ │ ├── swagger-ui-bundle.js
│ │ ├── swagger-ui-bundle.js.map
│ │ ├── swagger-ui-standalone-preset.js
│ │ ├── swagger-ui-standalone-preset.js.map
│ │ ├── swagger-ui.css
│ │ ├── swagger-ui.css.map
│ │ ├── swagger-ui.js
│ │ └── swagger-ui.js.map
│ └── testnet.conf
└── scala
│ ├── org
│ └── ergoplatform
│ │ ├── ErgoApp.scala
│ │ ├── GlobalConstants.scala
│ │ ├── http
│ │ ├── ErgoHttpService.scala
│ │ ├── NodePanelRoute.scala
│ │ ├── SwaggerRoute.scala
│ │ └── api
│ │ │ ├── ApiError.scala
│ │ │ ├── ApiExtraCodecs.scala
│ │ │ ├── ApiRequestsCodecs.scala
│ │ │ ├── BlockchainApiRoute.scala
│ │ │ ├── BlocksApiRoute.scala
│ │ │ ├── EmissionApiRoute.scala
│ │ │ ├── ErgoBaseApiRoute.scala
│ │ │ ├── ErgoPeersApiRoute.scala
│ │ │ ├── ErgoUtilsApiRoute.scala
│ │ │ ├── InfoApiRoute.scala
│ │ │ ├── MiningApiRoute.scala
│ │ │ ├── NipopowApiRoute.scala
│ │ │ ├── NodeApiRoute.scala
│ │ │ ├── ScanApiRoute.scala
│ │ │ ├── ScanEntities.scala
│ │ │ ├── ScriptApiRoute.scala
│ │ │ ├── SortDirection.scala
│ │ │ ├── TransactionsApiRoute.scala
│ │ │ ├── UtxoApiRoute.scala
│ │ │ ├── WalletApiOperations.scala
│ │ │ ├── WalletApiRoute.scala
│ │ │ └── requests
│ │ │ ├── CryptoResult.scala
│ │ │ ├── ExecuteRequest.scala
│ │ │ └── HintExtractionRequest.scala
│ │ ├── local
│ │ ├── CleanupWorker.scala
│ │ ├── ErgoStatsCollector.scala
│ │ └── MempoolAuditor.scala
│ │ ├── mining
│ │ ├── CandidateGenerator.scala
│ │ ├── ErgoMiner.scala
│ │ └── ErgoMiningThread.scala
│ │ ├── modifiers
│ │ ├── history
│ │ │ └── popow
│ │ │ │ └── NipopowProverWithDbAlgs.scala
│ │ └── mempool
│ │ │ └── UnconfirmedTransaction.scala
│ │ ├── network
│ │ ├── ElementPartitioner.scala
│ │ ├── ErgoNodeViewSynchronizer.scala
│ │ ├── ErgoNodeViewSynchronizerMessages.scala
│ │ ├── ErgoPeerStatus.scala
│ │ ├── ErgoSyncTracker.scala
│ │ ├── FixedSizeApproximateCacheQueue.scala
│ │ ├── VersionBasedPeerFilteringRule.scala
│ │ ├── message
│ │ │ ├── BasicMessagesRepo.scala
│ │ │ ├── Message.scala
│ │ │ └── MessageSerializer.scala
│ │ └── peer
│ │ │ ├── PeerDatabase.scala
│ │ │ ├── PeerInfo.scala
│ │ │ ├── PeerManager.scala
│ │ │ └── PenaltyType.scala
│ │ ├── nodeView
│ │ ├── ErgoModifiersCache.scala
│ │ ├── ErgoNodeViewHolder.scala
│ │ ├── ErgoReadersHolder.scala
│ │ ├── history
│ │ │ ├── ErgoHistory.scala
│ │ │ ├── ErgoHistoryReader.scala
│ │ │ ├── extra
│ │ │ │ ├── BalanceInfo.scala
│ │ │ │ ├── ExtraIndex.scala
│ │ │ │ ├── ExtraIndexSerializer.scala
│ │ │ │ ├── ExtraIndexer.scala
│ │ │ │ ├── IndexedErgoAddress.scala
│ │ │ │ ├── IndexedErgoBox.scala
│ │ │ │ ├── IndexedErgoTransaction.scala
│ │ │ │ ├── IndexedToken.scala
│ │ │ │ ├── IndexerState.scala
│ │ │ │ ├── NumericIndex.scala
│ │ │ │ └── Segment.scala
│ │ │ └── storage
│ │ │ │ ├── HistoryStorage.scala
│ │ │ │ └── modifierprocessors
│ │ │ │ ├── BasicReaders.scala
│ │ │ │ ├── BlockSectionProcessor.scala
│ │ │ │ ├── EmptyBlockSectionProcessor.scala
│ │ │ │ ├── FullBlockProcessor.scala
│ │ │ │ ├── FullBlockPruningProcessor.scala
│ │ │ │ ├── FullBlockSectionProcessor.scala
│ │ │ │ ├── HeadersProcessor.scala
│ │ │ │ ├── MinimalFullBlockHeightFunctions.scala
│ │ │ │ ├── PopowProcessor.scala
│ │ │ │ ├── ToDownloadProcessor.scala
│ │ │ │ ├── UtxoSetSnapshotDownloadPlan.scala
│ │ │ │ └── UtxoSetSnapshotProcessor.scala
│ │ ├── mempool
│ │ │ ├── ErgoMemPool.scala
│ │ │ ├── ErgoMemPoolReader.scala
│ │ │ ├── ErgoMemPoolUtils.scala
│ │ │ ├── ExpiringApproximateCache.scala
│ │ │ ├── FeeHistogramBin.scala
│ │ │ ├── HistogramStats.scala
│ │ │ ├── MemPoolStatistics.scala
│ │ │ └── OrderedTxPool.scala
│ │ ├── state
│ │ │ ├── BoxHolder.scala
│ │ │ ├── DigestState.scala
│ │ │ ├── ErgoState.scala
│ │ │ ├── ErgoStateReader.scala
│ │ │ ├── SnapshotsDb.scala
│ │ │ ├── SnapshotsInfo.scala
│ │ │ ├── UtxoSetSnapshotPersistence.scala
│ │ │ ├── UtxoState.scala
│ │ │ └── UtxoStateReader.scala
│ │ └── wallet
│ │ │ ├── AugWalletTransaction.scala
│ │ │ ├── BalancesSnapshot.scala
│ │ │ ├── ErgoAddressJsonEncoder.scala
│ │ │ ├── ErgoWallet.scala
│ │ │ ├── ErgoWalletActor.scala
│ │ │ ├── ErgoWalletActorMessages.scala
│ │ │ ├── ErgoWalletReader.scala
│ │ │ ├── ErgoWalletService.scala
│ │ │ ├── ErgoWalletServiceUtils.scala
│ │ │ ├── ErgoWalletState.scala
│ │ │ ├── ErgoWalletSupport.scala
│ │ │ ├── FilteringOptions.scala
│ │ │ ├── IdUtils.scala
│ │ │ ├── WalletBox.scala
│ │ │ ├── WalletCache.scala
│ │ │ ├── WalletProfile.scala
│ │ │ ├── WalletScanLogic.scala
│ │ │ ├── WalletTransaction.scala
│ │ │ ├── WalletVars.scala
│ │ │ ├── models
│ │ │ ├── ChangeBox.scala
│ │ │ └── CollectedBoxes.scala
│ │ │ ├── persistence
│ │ │ ├── Balance.scala
│ │ │ ├── OffChainRegistry.scala
│ │ │ ├── WalletDigest.scala
│ │ │ ├── WalletRegistry.scala
│ │ │ └── WalletStorage.scala
│ │ │ ├── requests
│ │ │ ├── AssetIssueRequest.scala
│ │ │ ├── BoxesRequest.scala
│ │ │ ├── BurnTokensRequest.scala
│ │ │ ├── ExternalSecret.scala
│ │ │ ├── GenerateCommitmentsRequest.scala
│ │ │ ├── PaymentRequest.scala
│ │ │ ├── RequestsHolder.scala
│ │ │ ├── TransactionGenerationRequest.scala
│ │ │ └── TransactionSigningRequest.scala
│ │ │ └── scanning
│ │ │ ├── Scan.scala
│ │ │ ├── ScanWalletInteraction.scala
│ │ │ ├── ScanningPredicate.scala
│ │ │ ├── ScanningPredicateJsonCodecs.scala
│ │ │ └── ScanningPredicateSerializer.scala
│ │ ├── reemission
│ │ └── ReemissionRulesUtils.scala
│ │ ├── settings
│ │ ├── Args.scala
│ │ ├── CacheSettings.scala
│ │ ├── ErgoSettings.scala
│ │ ├── ErgoSettingsReader.scala
│ │ ├── NetworkType.scala
│ │ ├── NodeConfigurationSettings.scala
│ │ ├── StateTypeReaders.scala
│ │ ├── VotingTargets.scala
│ │ └── WalletSettings.scala
│ │ └── tools
│ │ ├── CoinEmissionPrinter.scala
│ │ └── ValidationRulesPrinter.scala
│ └── scorex
│ ├── core
│ ├── ModifiersCache.scala
│ ├── api
│ │ └── http
│ │ │ ├── ApiDirectives.scala
│ │ │ ├── ApiErrorHandler.scala
│ │ │ ├── ApiRejectionHandler.scala
│ │ │ ├── ApiResponse.scala
│ │ │ ├── ApiRoute.scala
│ │ │ ├── CompositeHttpService.scala
│ │ │ ├── CorsHandler.scala
│ │ │ └── swagger
│ │ │ └── SwaggerConfigRoute.scala
│ ├── app
│ │ └── ScorexContext.scala
│ ├── network
│ │ ├── ConnectedPeer.scala
│ │ ├── ConnectionDescription.scala
│ │ ├── ConnectionDirection.scala
│ │ ├── ConnectionId.scala
│ │ ├── DeliveryTracker.scala
│ │ ├── MaliciousBehaviorException.scala
│ │ ├── ModifiersStatus.scala
│ │ ├── NetworkController.scala
│ │ ├── PeerConnectionHandler.scala
│ │ ├── PeerSynchronizer.scala
│ │ ├── SendingStrategy.scala
│ │ ├── Synchronizer.scala
│ │ └── UPnP.scala
│ └── utils
│ │ ├── ActorHelper.scala
│ │ └── NetworkUtils.scala
│ └── util
│ └── serialization
│ ├── VLQByteStringReader.scala
│ └── VLQByteStringWriter.scala
└── test
├── resources
├── application.conf
├── difficulty.csv
├── execute-script.json
├── logback-test.xml
├── settings.conf
└── settings.json
└── scala
├── org
└── ergoplatform
│ ├── db
│ ├── DBSpec.scala
│ ├── KvStoreReaderSpec.scala
│ ├── LDBKVStoreSpec.scala
│ └── VersionedStoreSpec.scala
│ ├── http
│ └── routes
│ │ ├── BlocksApiRouteSpec.scala
│ │ ├── EmissionApiRouteSpec.scala
│ │ ├── ErgoPeersApiRouteSpec.scala
│ │ ├── InfoApiRoutesSpec.scala
│ │ ├── MiningApiRouteSpec.scala
│ │ ├── NipopowApiRoutesSpec.scala
│ │ ├── ScanApiRouteSpec.scala
│ │ ├── ScriptApiRouteSpec.scala
│ │ ├── TransactionApiRouteSpec.scala
│ │ ├── UtilsApiRouteSpec.scala
│ │ ├── UtxoApiRouteSpec.scala
│ │ └── WalletApiRouteSpec.scala
│ ├── local
│ ├── MempoolAuditorSpec.scala
│ └── NipopowVerifierSpec.scala
│ ├── mining
│ ├── CandidateGeneratorPropSpec.scala
│ ├── CandidateGeneratorSpec.scala
│ └── ErgoMinerSpec.scala
│ ├── modifiers
│ ├── history
│ │ ├── HeadersSpec.scala
│ │ ├── PoPowAlgosSpec.scala
│ │ └── PoPowAlgosWithDBSpec.scala
│ └── mempool
│ │ ├── ErgoNodeTransactionSpec.scala
│ │ └── ExpirationSpecification.scala
│ ├── network
│ ├── ActivePeerFilteringSpecification.scala
│ ├── DeliveryTrackerSpec.scala
│ ├── ElementPartitionerSpecification.scala
│ ├── ErgoNodeViewSynchronizerSpecification.scala
│ ├── ErgoSyncInfoSpecification.scala
│ ├── ErgoSyncTrackerSpecification.scala
│ ├── FixedSizeApproximateCacheQueueSpec.scala
│ ├── InvSpecification.scala
│ ├── ModifiersSpecification.scala
│ ├── PeerFilteringRuleSpecification.scala
│ └── RequestModifiersSpecification.scala
│ ├── nodeView
│ ├── ErgoModifiersCacheSpec.scala
│ ├── NodeViewSynchronizerTests.scala
│ ├── history
│ │ ├── BlockSectionValidationSpecification.scala
│ │ ├── NonVerifyADHistorySpecification.scala
│ │ ├── PopowProcessorSpecification.scala
│ │ ├── UtxoSetSnapshotProcessorSpecification.scala
│ │ ├── VerifyADHistorySpecification.scala
│ │ ├── VerifyNonADHistorySpecification.scala
│ │ ├── extra
│ │ │ ├── ChainGenerator.scala
│ │ │ ├── ExtraIndexerSpecification.scala
│ │ │ ├── ExtraIndexerTestActor.scala
│ │ │ └── SegmentSpec.scala
│ │ └── storage
│ │ │ └── HistoryStorageSpec.scala
│ ├── mempool
│ │ ├── ErgoMemPoolSpec.scala
│ │ ├── ExpiringApproximateCacheSpec.scala
│ │ └── ScriptsSpec.scala
│ ├── state
│ │ ├── DigestStateSpecification.scala
│ │ ├── ErgoStateContextSpec.scala
│ │ ├── ErgoStateSpecification.scala
│ │ ├── SnapshotsDbSpecification.scala
│ │ ├── SnapshotsInfoSpecification.scala
│ │ ├── UtxoStateSpecification.scala
│ │ └── wrapped
│ │ │ ├── WrappedDigestState.scala
│ │ │ └── WrappedUtxoState.scala
│ ├── viewholder
│ │ ├── ErgoNodeViewHolderSpec.scala
│ │ └── PrunedNodeViewHolderSpec.scala
│ └── wallet
│ │ ├── ErgoWalletServiceSpec.scala
│ │ ├── ErgoWalletSpec.scala
│ │ ├── WalletProfileSpec.scala
│ │ ├── WalletScanLogicSpec.scala
│ │ ├── WalletVarsSpec.scala
│ │ ├── persistence
│ │ ├── OffChainRegistrySpec.scala
│ │ ├── WalletRegistryBenchmark.scala
│ │ ├── WalletRegistrySpec.scala
│ │ └── WalletStorageSpec.scala
│ │ └── scanning
│ │ ├── ScanSpecification.scala
│ │ ├── ScanningPredicateJsonCodecsSpecification.scala
│ │ ├── ScanningPredicateSerializerSpecification.scala
│ │ └── ScanningPredicateSpecification.scala
│ ├── sanity
│ ├── ErgoSanity.scala
│ ├── ErgoSanityDigest.scala
│ └── ErgoSanityUTXO.scala
│ ├── serialization
│ ├── ErgoBoxSerializerSpec.scala
│ ├── JsonSerializationSpec.scala
│ └── SerializationTests.scala
│ ├── settings
│ ├── ErgoNodeVotingSpecification.scala
│ └── ErgoSettingsSpecification.scala
│ ├── tools
│ ├── ChainGenerator.scala
│ ├── ConfigGenerator.scala
│ ├── DefaultParametersPrinter.scala
│ ├── DifficultyControlSimulator.scala
│ ├── FeeSimulator.scala
│ ├── MinerBench.scala
│ ├── emission.csv
│ └── emissionPlot.gnu
│ └── utils
│ ├── ErgoNodeTestConstants.scala
│ ├── ErgoTestHelpers.scala
│ ├── HistoryTestHelpers.scala
│ ├── MempoolTestHelpers.scala
│ ├── NodeViewTestConfig.scala
│ ├── NodeViewTestContext.scala
│ ├── NodeViewTestOps.scala
│ ├── Stubs.scala
│ ├── TestCase.scala
│ ├── WalletTestOps.scala
│ ├── fixtures
│ ├── NodeViewFixture.scala
│ ├── SequentialAkkaFixture.scala
│ └── WalletFixture.scala
│ └── generators
│ ├── ChainGenerator.scala
│ ├── ConnectedPeerGenerators.scala
│ ├── ErgoNodeGenerators.scala
│ ├── ErgoNodeTransactionGenerators.scala
│ ├── ErgoNodeWalletGenerators.scala
│ └── ValidBlocksGenerators.scala
└── scorex
├── core
└── network
│ ├── DeliveryTrackerSpec.scala
│ └── PeerSpecSerializerSpec.scala
└── testkit
├── TestkitHelpers.scala
├── generators
├── AllModifierProducers.scala
├── ArbitraryTransactionsCarryingModifierProducer.scala
├── CustomModifierProducer.scala
├── SemanticallyInvalidModifierProducer.scala
├── SemanticallyValidModifierProducer.scala
├── SemanticallyValidTransactionsCarryingModifier.scala
├── SyntacticallyTargetedModifierProducer.scala
└── TotallyValidModifierProducer.scala
├── properties
├── HistoryTests.scala
├── NodeViewHolderTests.scala
├── mempool
│ ├── MemoryPoolTest.scala
│ ├── MempoolFilterPerformanceTest.scala
│ ├── MempoolRemovalTest.scala
│ └── MempoolTransactionsTest.scala
└── state
│ ├── StateApplicationTest.scala
│ └── StateTests.scala
└── utils
└── AkkaFixture.scala
/.dockerignore:
--------------------------------------------------------------------------------
1 | **/.gitignore
2 | **/.gitkeep
3 | **/.dockerignore
4 |
5 | # build helpers
6 | Makefile
7 | Dockerfile
8 | docker-compose.yml
9 |
10 | # other
11 | .idea/
12 | .ensime
13 | .vscode/
14 | .ivy2/
15 | .sbt/
16 |
17 | # ergo binary/cache
18 | /target/
19 | /ergo.jar
20 | /ergo/
21 | /.ergo/
22 |
23 | #
24 | README.md
25 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | ; This file is for unifying the coding style for different editors and IDEs.
2 | ; More information at http://EditorConfig.org
3 |
4 | root = true
5 |
6 | [*.scala]
7 | end_of_line = LF
8 | indent_style = space
9 | indent_size = 2
10 | insert_final_newline = true
11 |
--------------------------------------------------------------------------------
/.github/workflows/docker-publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish Docker image
2 |
3 | on:
4 | release:
5 | types: [published]
6 |
7 | jobs:
8 | push_to_registry:
9 | name: Push Docker image to Docker Hub
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Check out the repo
13 | uses: actions/checkout@v2
14 |
15 | - name: qemu
16 | uses: docker/setup-qemu-action@v1
17 |
18 | - uses: docker/setup-buildx-action@v1
19 |
20 | - name: Docker Login
21 | uses: docker/login-action@v1.10.0
22 | with:
23 | username: ${{ secrets.DOCKER_USERNAME }}
24 | password: ${{ secrets.DOCKER_PASSWORD }}
25 |
26 | - name: Docker Metadata action
27 | uses: docker/metadata-action@v3.5.0
28 | id: meta
29 | with:
30 | images: ergoplatform/ergo
31 |
32 | - name: Build and push Docker images
33 | uses: docker/build-push-action@v2.7.0
34 | with:
35 | context: .
36 | push: true
37 | platforms: linux/amd64,linux/arm64
38 | tags: ${{ steps.meta.outputs.tags }}
39 | labels: ${{ steps.meta.outputs.labels }}
40 |
--------------------------------------------------------------------------------
/.github/workflows/release-binaries.yml:
--------------------------------------------------------------------------------
1 | name: Publish release-binaries
2 |
3 | on:
4 | release:
5 | types: [published]
6 |
7 | env:
8 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
9 |
10 | jobs:
11 | release-binaries:
12 | name: Publish release binaries
13 | runs-on: macos-latest
14 | env:
15 | ERGO_RELEASE_PLATFORM: macos-x64
16 | ERGO_RELEASE_TAG: ${{ github.event.release.tag_name }}
17 | steps:
18 | - uses: actions/checkout@v4
19 | - uses: actions/setup-python@v4
20 | - name: Download ergo node jar
21 | run: |
22 | echo $GITHUB_REF
23 | gh release download $ERGO_RELEASE_TAG -p "ergo*"
24 | - name: Create release binary files
25 | run: python ci/release-binaries.py
26 | - name: Put binary files into release
27 | run: gh release upload $ERGO_RELEASE_TAG $(echo $(find release -name "ergo-node-*"))
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | mainnet
2 | devnet
3 |
4 | # IDE/Editor files
5 | .idea
6 | .bsp
7 | .ensime
8 | .ensime_cache/
9 | scorex.yaml
10 |
11 | # scala build folders
12 | target
13 |
14 | # standalone docker
15 | !Dockerfile
16 |
17 | # local application.conf
18 | local-application.conf
19 | *-local*.conf
20 | my*.conf
21 |
22 | # logs
23 | *.log
24 | *.log.gz
25 | *.tmp
26 |
27 | # binary files
28 | *.pdf
29 | *.gz
30 |
31 | # Core latex/pdflatex auxiliary files:
32 | *.aux
33 | *.lof
34 | *.lot
35 | *.fls
36 | *.out
37 | *.toc
38 | *.fmt
39 | *.fot
40 | *.cb
41 | *.cb2
42 | *.bbl
43 | *.blg
44 |
45 | # db files
46 | *.data
47 | ergo/
48 | .ergo/
49 | .ergo_test/
50 |
51 | # for temporary experiments
52 | sheet.scala
53 | project/project/
54 |
55 | local.conf
56 |
57 | # osx files
58 | .DS_Store
59 |
60 | # metals, bloop, vscode
61 | .bloop/
62 | .metals/
63 | .vscode/
64 | project/metals.sbt
65 | null/
66 | out/
67 |
68 | # scala worksheets
69 | *.worksheet.sc
--------------------------------------------------------------------------------
/.scalafmt.conf:
--------------------------------------------------------------------------------
1 | version = "2.3.2"
2 | style = defaultWithAlign
3 |
4 | align.openParenCallSite = false
5 | align.openParenDefnSite = false
6 | align.arrowEnumeratorGenerator = true
7 | align.tokens = [
8 | {code = "="},
9 | {code = "->"},
10 | {code = "<-"},
11 | {code = "=>", owner = "Case"},
12 | {code = "%", owner = "Term.ApplyInfix"},
13 | {code = "%%", owner = "Term.ApplyInfix"}
14 | ]
15 | continuationIndent.callSite = 2
16 | continuationIndent.defnSite = 2
17 | continuationIndent.extendSite = 2
18 | danglingParentheses = true
19 | indentOperator = spray
20 | maxColumn = 90
21 | newlines.alwaysBeforeTopLevelStatements = true
22 | project.excludeFilters = [".*\\.sbt"]
23 | rewrite.rules = [SortImports, SortModifiers]
24 | rewrite.sortModifiers.order = [
25 | "implicit", "final", "sealed", "abstract",
26 | "override", "private", "protected", "lazy"
27 | ]
28 | rewrite.redundantBraces.stringInterpolation = true
29 | spaces.inImportCurlyBraces = false
30 | unindentTopLevelOperators = true
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM sbtscala/scala-sbt:eclipse-temurin-11.0.15_1.7.1_2.13.8 as builder
2 | WORKDIR /mnt
3 | COPY build.sbt findbugs-exclude.xml ./
4 | COPY project/ project/
5 | COPY avldb/build.sbt avldb/build.sbt
6 | COPY avldb/project/ avldb/project/
7 | COPY ergo-core/build.sbt ergo-core/build.sbt
8 | COPY ergo-core/project/ ergo-core/project/
9 | COPY ergo-wallet/build.sbt ergo-wallet/build.sbt
10 | COPY ergo-wallet/project/ ergo-wallet/project/
11 | RUN sbt update
12 | COPY . ./
13 | RUN sbt assembly
14 | RUN mv `find target/scala-*/stripped/ -name ergo-*.jar` ergo.jar
15 |
16 | FROM eclipse-temurin:11-jre-jammy
17 | RUN apt-get update && apt-get install -y curl jq && rm -rf /var/lib/apt/lists/*
18 | RUN adduser --disabled-password --home /home/ergo --uid 9052 --gecos "ErgoPlatform" ergo && \
19 | install -m 0750 -o ergo -g ergo -d /home/ergo/.ergo
20 | USER ergo
21 | EXPOSE 9020 9021 9022 9052 9030 9053
22 | WORKDIR /home/ergo
23 | VOLUME ["/home/ergo/.ergo"]
24 | ENV MAX_HEAP 3G
25 | ENV _JAVA_OPTIONS "-Xms${MAX_HEAP} -Xmx${MAX_HEAP}"
26 | COPY --from=builder /mnt/ergo.jar /home/ergo/ergo.jar
27 | ENTRYPOINT ["java", "-jar", "/home/ergo/ergo.jar"]
28 |
--------------------------------------------------------------------------------
/avldb/.gitignore:
--------------------------------------------------------------------------------
1 | # IDE/Editor files
2 | .idea
3 | .ensime
4 | .ensime_cache/
5 | scorex.yaml
6 |
7 | # scala build folders
8 | target
9 |
10 | # dotfiles
11 | .dockerignore
12 | .editorconfig
13 |
14 | # standalone docker
15 | Dockerfile
16 |
17 | # logs
18 | scorex-errors.log
19 |
20 | #jmh-report
21 | benchmarks/jmh-result.csv
22 |
--------------------------------------------------------------------------------
/avldb/.travis.yml:
--------------------------------------------------------------------------------
1 | language: scala
2 | jdk:
3 | - oraclejdk9
4 | scala:
5 | - 2.12.10
6 | script:
7 | - sbt test
8 | branches:
9 | only:
10 | - master
11 | - /^\d\.\d+$/
12 | # These directories are cached to S3 at the end of the build
13 | cache:
14 | directories:
15 | - $HOME/.ivy2/cache
16 | - $HOME/.sbt
17 | before_cache:
18 | # Cleanup the cached directories to avoid unnecessary cache updates
19 | - find $HOME/.ivy2/cache -name "ivydata-*.properties" -print -delete
20 | - find $HOME/.sbt -name "*.lock" -print -delete
21 |
--------------------------------------------------------------------------------
/avldb/README.md:
--------------------------------------------------------------------------------
1 | # AVL-DB
2 |
3 | To run benchmarks use command `sbt "project benchmarks" "jmh:run -i 3 -wi 3 -f1 -t1 -rf csv .*AVLTreeBatchPerformance"`
4 |
5 | Results will be stored in `benchmarks/jmh-results.csv` file.
--------------------------------------------------------------------------------
/avldb/benchmarks/src/main/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | System.out
6 |
7 | INFO
8 |
9 |
10 | [%thread] >> [%-5level] %logger{36} >> %d{HH:mm:ss.SSS} %msg%n
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/avldb/benchmarks/src/test/scala/CsvReporter.scala:
--------------------------------------------------------------------------------
1 | import java.io.PrintWriter
2 |
3 | import org.scalameter.{CurveData, Persistor, log}
4 | import org.scalameter.api.Reporter
5 | import org.scalameter.utils.Tree
6 |
7 | class CsvReporter extends Reporter[Double] {
8 |
9 | override def report(result: CurveData[Double], persistor: Persistor): Unit = {
10 | for (measurement <- result.measurements) {
11 | log(s"${measurement.params}: ${measurement.value} ${measurement.units}")
12 | }
13 |
14 | val writer = new PrintWriter("target/memory_avltree_results.csv")
15 | val headerLine = result.measurements.map(_.params.axisData.head._2.toString).mkString(",")
16 | val valuesLine = result.measurements.map(_.value).mkString(",")
17 | writer.println(headerLine)
18 | writer.println(valuesLine)
19 | writer.flush()
20 | writer.close()
21 | }
22 |
23 | override def report(results: Tree[CurveData[Double]], persistor: Persistor): Boolean = true
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/avldb/benchmarks/src/test/scala/MemoryFootprint.scala:
--------------------------------------------------------------------------------
1 | import org.scalameter
2 | import org.scalameter.Key._
3 | import org.scalameter.api.Bench
4 | import org.scalameter.execution.SeparateJvmsExecutor
5 | import org.scalameter.picklers.Implicits._
6 | import org.scalameter.{Aggregator, Executor, Gen, Persistor}
7 | import scorex.crypto.authds.benchmarks.Helper._
8 |
9 | class MemoryFootprint extends Bench[Double] {
10 |
11 | lazy val executor = SeparateJvmsExecutor(
12 | new Executor.Warmer.Default,
13 | Aggregator.median[Double],
14 | measurer)
15 | lazy val measurer = new scalameter.Measurer.MemoryFootprint
16 | lazy val reporter = new CsvReporter()
17 | lazy val persistor = Persistor.None
18 |
19 | val sizes = Gen.enumeration("size")(100000, 500000, 1000000)
20 |
21 | performance of "MemoryFootprint" in {
22 | performance of "AVLTree" in {
23 | using(sizes) config(
24 | exec.benchRuns -> 4,
25 | exec.independentSamples -> 2
26 | ) in { createProver }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/avldb/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=1.2.8
2 |
--------------------------------------------------------------------------------
/avldb/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | logLevel := Level.Warn
2 |
3 | addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.0")
4 |
5 | addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0")
6 |
7 | addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.5.1")
8 |
9 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.5")
10 |
11 | addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.3.3")
12 |
13 | addSbtPlugin("net.bzzt" % "sbt-reproducible-builds" % "0.25")
--------------------------------------------------------------------------------
/avldb/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | System.out
6 |
7 | INFO
8 |
9 |
10 | [%thread] >> [%-5level] %logger{36} >> %d{HH:mm:ss.SSS} %msg%n
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/avldb/src/main/scala/org/ergoplatform/serialization/ErgoSerializer.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.serialization
2 |
3 | import java.nio.ByteBuffer
4 | import scorex.util.ByteArrayBuilder
5 | import scorex.util.serialization._
6 |
7 | import scala.util.Try
8 |
9 | /**
10 | * Basic interface for serializer with additional methods to work with bytes, not only Reader/Writer instances
11 | */
12 | trait ErgoSerializer[T] extends Serializer[T, T, Reader, Writer] {
13 |
14 | /**
15 | * Serialize object `obj` to byte array
16 | */
17 | def toBytes(obj: T): Array[Byte] = {
18 | val writer = new VLQByteBufferWriter(new ByteArrayBuilder())
19 | serialize(obj, writer)
20 | writer.result().toBytes
21 | }
22 |
23 |
24 | /**
25 | * Deserialize byte array into object of type `T` (or throw exception)
26 | */
27 | def parseBytes(bytes: Array[Byte]): T = {
28 | val reader = new VLQByteBufferReader(ByteBuffer.wrap(bytes))
29 | parse(reader)
30 | }
31 |
32 | /**
33 | * Deserialize byte array into object of type `T` (or return Failure)
34 | */
35 | def parseBytesTry(bytes: Array[Byte]): Try[T] = {
36 | Try(parseBytes(bytes))
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/AvlTreeParameters.scala:
--------------------------------------------------------------------------------
1 | package scorex.crypto.authds.avltree.batch
2 |
3 | import Constants.HashLength
4 |
5 | /**
6 | * Parameters of AVL+ tree nodes (internal and leaves)
7 | * @param keySize - size of a key (fixed)
8 | * @param valueSize - size of a value in a leaf (fixed, if defined, arbitrary, if None)
9 | * @param labelSize - size of a label (node hash), fixed
10 | */
11 | case class AvlTreeParameters(keySize: Int, valueSize: Option[Int], labelSize: Int) {
12 | /**
13 | * @return whether value is fixed-size
14 | */
15 | def fixedSizeValue: Boolean = valueSize.isDefined
16 | }
17 |
18 |
19 | /**
20 | * AVL+ tree node parameters. The tree is used to authenticate UTXO set.
21 | * Keys and hashes are 256-bits long, values are boxes, so value size is dynamic.
22 | */
23 | object StateTreeParameters extends AvlTreeParameters(keySize = HashLength, valueSize = None, labelSize = HashLength)
24 |
--------------------------------------------------------------------------------
/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/Constants.scala:
--------------------------------------------------------------------------------
1 | package scorex.crypto.authds.avltree.batch
2 |
3 | import scorex.crypto.hash.{Blake2b256, Digest32}
4 |
5 |
6 | /**
7 | * Commonly used constants
8 | *
9 | * //todo: move to core module once it is established
10 | */
11 | object Constants {
12 | /**
13 | * Type of hash function output
14 | */
15 | type DigestType = Digest32
16 |
17 | /**
18 | * Length of hash function output
19 | */
20 | val HashLength: Int = 32
21 |
22 | /**
23 | * Type of hash function used in the protocol
24 | */
25 | type HashFnType = Blake2b256.type
26 |
27 | /**
28 | * Thread-safe instance of hash function used in the protocol
29 | */
30 | val hashFn: HashFnType = Blake2b256
31 | }
32 |
--------------------------------------------------------------------------------
/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/LDBVersionedStoreBenchmark.scala:
--------------------------------------------------------------------------------
1 | package scorex.crypto.authds.avltree.batch.benchmark
2 |
3 | import com.google.common.primitives.Longs
4 | import scorex.crypto.authds.avltree.batch.helpers.FileHelper
5 | import scorex.utils.Random
6 | import scorex.db.LDBVersionedStore
7 |
8 | object LDBVersionedStoreBenchmark extends App with FileHelper {
9 | val KL = 32
10 | val VL = 8
11 | val LL = 32
12 | val NumMods = 2000000
13 | val Step = 1000
14 |
15 | val store = new LDBVersionedStore(getRandomTempDir, 10)
16 | val mods = generateModifications()
17 | var currentVersion: Option[Long] = None
18 |
19 | (0 until(NumMods, Step)) foreach { i =>
20 | println(i)
21 | val mod: Seq[(Array[Byte], Array[Byte])] = mods.slice(i, i + Step)
22 | val nextVersion = Longs.toByteArray(i)
23 | store.update(nextVersion, Seq(), mod).get
24 | currentVersion.foreach(v => {
25 | store.rollbackTo(Longs.toByteArray(v))
26 | store.update(nextVersion, Seq(), mod).get
27 | })
28 | currentVersion = Some(i)
29 |
30 | mods.slice(0, i + Step).foreach { m =>
31 | store(m._1)
32 | }
33 | }
34 |
35 | def generateModifications(): Array[(Array[Byte], Array[Byte])] = {
36 | val mods = new Array[(Array[Byte], Array[Byte])](NumMods)
37 |
38 | for (i <- 0 until NumMods) {
39 | mods(i) = (Random.randomBytes(KL), Random.randomBytes(VL))
40 | }
41 | mods
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/OOMTest.scala.rej:
--------------------------------------------------------------------------------
1 | --- avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/OOMTest.scala 2019-11-14 10:16:12.534328295 +0300
2 | +++ avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/OOMTest.scala 2019-11-11 12:10:35.330941786 +0300
3 | @@ -7,8 +7,8 @@
4 | import io.iohk.iodb.{ByteArrayWrapper, LSMStore}
5 | import scorex.crypto.authds.{ADDigest, ADKey, ADValue}
6 | import scorex.crypto.authds.avltree.batch._
7 | -import scorex.crypto.encode.Base16
8 | +import scorex.util.encode.Base16
9 | import scorex.crypto.hash.{Blake2b256, Digest32}
10 | import scala.collection.immutable.SortedMap
11 |
12 | object OOMTest extends App {
13 |
--------------------------------------------------------------------------------
/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/helpers/FileHelper.scala:
--------------------------------------------------------------------------------
1 | package scorex.crypto.authds.avltree.batch.helpers
2 |
3 | import java.io.File
4 |
5 | import scala.util.Random
6 |
7 | trait FileHelper {
8 |
9 | def getRandomTempDir: File = {
10 | val dir = java.nio.file.Files.createTempDirectory("avldb_test_" + Random.alphanumeric.take(15).mkString).toFile
11 | dir.deleteOnExit()
12 | dir
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/ci/ergo.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ergoplatform/ergo/fb0d3098ffe95a40057b42f8e68b25ae4dfa7f03/ci/ergo.icns
--------------------------------------------------------------------------------
/ci/import_gpg.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # setting up gpg2 for reading passphrase from parameters
3 | # via https://github.com/beautiful-scala/scalastyle/blob/master/.github/workflows/release.yml#L16
4 | # from https://github.com/olafurpg/sbt-ci-release/issues/95
5 |
6 | # setup gpg
7 | mkdir ~/.gnupg && chmod 700 ~/.gnupg
8 | echo use-agent >> ~/.gnupg/gpg.conf
9 | echo pinentry-mode loopback >> ~/.gnupg/gpg.conf
10 | echo allow-loopback-pinentry >> ~/.gnupg/gpg-agent.conf
11 | chmod 600 ~/.gnupg/*
12 | echo RELOADAGENT | gpg-connect-agent
13 |
14 | # decode key
15 | # private key should be previously exported with:
16 | # gpg --export-secret-keys [id] | base64 | pbcopy
17 | # and stored as github repository secret under the following name (see env var name below)
18 | printf "$GPG_SIGNING_KEY" | base64 --decode > ~/.gnupg/private.key
19 |
20 | # import key
21 | gpg --no-tty --batch --yes --import ~/.gnupg/private.key
22 |
--------------------------------------------------------------------------------
/ergo-core/build.sbt:
--------------------------------------------------------------------------------
1 | // this values should be in sync with root (i.e. ../build.sbt)
2 | val scala211 = "2.11.12"
3 | val scala212 = "2.12.20"
4 | val scala213 = "2.13.16"
5 |
6 | val deps211 = Seq(
7 | "io.circe" %% "circe-core" % "0.10.0",
8 | "io.circe" %% "circe-generic" % "0.10.0",
9 | "io.circe" %% "circe-parser" % "0.10.0")
10 | val deps212 = Seq(
11 | "io.circe" %% "circe-core" % "0.13.0",
12 | "io.circe" %% "circe-generic" % "0.13.0",
13 | "io.circe" %% "circe-parser" % "0.13.0")
14 |
15 | publishMavenStyle := true
16 | Test / publishArtifact := false
17 |
18 | libraryDependencies ++= Seq() ++
19 | (if (scalaVersion.value == scala211) deps211 else deps212)
20 |
21 | scalacOptions ++= (if (scalaBinaryVersion.value == scala211) Seq("-language:implicitConversions") else Seq())
22 | scalacOptions --= Seq("-Ywarn-numeric-widen", "-Ywarn-value-discard", "-Ywarn-unused:params", "-Xfatal-warnings")
--------------------------------------------------------------------------------
/ergo-core/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version = 1.2.8
--------------------------------------------------------------------------------
/ergo-core/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.0")
2 | addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "2.0")
3 | addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.8")
4 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/CriticalSystemException.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform
2 |
3 | /** Exception that triggers system shutdown */
4 | case class CriticalSystemException(message: String) extends Exception(message)
5 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/NodeViewComponent.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform
2 |
3 | trait NodeViewComponent
4 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/consensus/ContainsModifiers.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.consensus
2 |
3 | import org.ergoplatform.modifiers.ErgoNodeViewModifier
4 | import scorex.util.ModifierId
5 |
6 | /**
7 | * Object that contains modifiers of type `MOD`
8 | */
9 | trait ContainsModifiers[MOD <: ErgoNodeViewModifier] {
10 |
11 | /**
12 | *
13 | * @param persistentModifier - modifier to check
14 | * @return `true` if this object contains this modifier, `false` otherwise
15 | */
16 | def contains(persistentModifier: MOD): Boolean = contains(persistentModifier.id)
17 |
18 | /**
19 | *
20 | * @param id - modifier's id
21 | * @return `true` if this object contains modifier with specified id, `false` otherwise
22 | */
23 | def contains(id: ModifierId): Boolean = modifierById(id).isDefined
24 |
25 | /**
26 | * @param modifierId - modifier id to get
27 | * @return modifier of type MOD with id == modifierId if exist
28 | */
29 | def modifierById(modifierId: ModifierId): Option[MOD]
30 | }
31 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/consensus/ModifierSemanticValidity.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.consensus
2 |
3 | /**
4 | * Outcome of modifier semantic validation
5 | */
6 | sealed trait ModifierSemanticValidity {
7 | val code: Byte
8 | }
9 |
10 | object ModifierSemanticValidity {
11 |
12 | case object Absent extends ModifierSemanticValidity {
13 | override val code: Byte = 0
14 | }
15 |
16 | case object Unknown extends ModifierSemanticValidity {
17 | override val code: Byte = 1
18 | }
19 |
20 | case object Valid extends ModifierSemanticValidity {
21 | override val code: Byte = 2
22 | }
23 |
24 | case object Invalid extends ModifierSemanticValidity {
25 | override val code: Byte = 3
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/consensus/PeerChainStatus.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.consensus
2 |
3 | /**
4 | * Status of a peer's chain relatively to our
5 | */
6 | sealed trait PeerChainStatus
7 |
8 | /**
9 | * Peer has the same latest reported block as our best block
10 | */
11 | case object Equal extends PeerChainStatus
12 |
13 | /**
14 | * Peer's best block is in our best chain, but we have continuation of it
15 | */
16 | case object Younger extends PeerChainStatus
17 |
18 | /**
19 | * Peer has another block on the same height as our best block (and we know a common block)
20 | */
21 | case object Fork extends PeerChainStatus
22 |
23 | /**
24 | * Peer's chain is seemingly more developed
25 | */
26 | case object Older extends PeerChainStatus
27 |
28 | /**
29 | * Peer is likely trying to fool us, or its chain is confusing in regards with comparing to our
30 | */
31 | case object Nonsense extends PeerChainStatus
32 |
33 | /**
34 | * We do not know about peer's chain yet
35 | */
36 | case object Unknown extends PeerChainStatus
37 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/consensus/ProgressInfo.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.consensus
2 |
3 | import org.ergoplatform.modifiers.{BlockSection, NetworkObjectTypeId}
4 | import org.ergoplatform.utils.ScorexEncoder
5 | import scorex.util.ModifierId
6 |
7 | /**
8 | * Info returned by history to nodeViewHolder after modifier application
9 | *
10 | * @param branchPoint - branch point in case of rollback
11 | * @param toRemove - modifiers to remove from current node view
12 | * @param toApply - modifiers to apply to current node view
13 | * @param toDownload - modifiers to download from other nodes
14 | * @tparam PM - type of used modifier
15 | */
16 | case class ProgressInfo[PM <: BlockSection](branchPoint: Option[ModifierId],
17 | toRemove: Seq[PM],
18 | toApply: Seq[PM],
19 | toDownload: Seq[(NetworkObjectTypeId.Value, ModifierId)])
20 | (implicit encoder: ScorexEncoder) {
21 |
22 | if (toRemove.nonEmpty)
23 | require(branchPoint.isDefined, s"Branch point should be defined for non-empty `toRemove`")
24 |
25 | lazy val chainSwitchingNeeded: Boolean = toRemove.nonEmpty
26 |
27 | override def toString: String = {
28 | s"ProgressInfo(BranchPoint: ${branchPoint.map(encoder.encodeId)}, " +
29 | s" to remove: ${toRemove.map(_.encodedId)}, to apply: ${toApply.map(_.encodedId)})"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/consensus/SyncInfo.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.consensus
2 |
3 | import org.ergoplatform.core.BytesSerializable
4 |
5 | /**
6 | * Syncing info provides information about starting points this node recommends another to start
7 | * synchronization from
8 | */
9 | trait SyncInfo extends BytesSerializable
10 |
11 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/core/BytesSerializable.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.core
2 |
3 | import org.ergoplatform.serialization.ErgoSerializer
4 |
5 | /**
6 | * Basic interface for objects which can be represented as bytes
7 | */
8 | trait BytesSerializable extends Serializable {
9 |
10 | type M >: this.type <: BytesSerializable
11 |
12 | def bytes: Array[Byte] = serializer.toBytes(this)
13 |
14 | /**
15 | * Serializer which can convert self to bytes
16 | */
17 | def serializer: ErgoSerializer[M]
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/local/NipopowVerificationResult.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.local
2 |
3 | /**
4 | * Hierarchy of outcomes when another nipopow proof is compared with one which is known to be best to the moment
5 | * of comparison
6 | */
7 | sealed trait NipopowProofVerificationResult
8 |
9 | /**
10 | * Basic interface for valid proof verification results
11 | */
12 | sealed trait CorrectNipopowProofVerificationResult extends NipopowProofVerificationResult {
13 | /**
14 | * Total number of known valid proofs verified, including this one
15 | */
16 | val totalProofsProcessed: Int
17 | }
18 |
19 | /**
20 | * Presented nipopow proof is better than known one
21 | */
22 | case class BetterChain(override val totalProofsProcessed: Int) extends CorrectNipopowProofVerificationResult
23 |
24 | /**
25 | * Presented nipopow proof is no better than known one
26 | */
27 | case class NoBetterChain(override val totalProofsProcessed: Int) extends CorrectNipopowProofVerificationResult
28 |
29 | /**
30 | * Presented nipopow proof is not valid
31 | */
32 | case object ValidationError extends NipopowProofVerificationResult
33 |
34 |
35 | /**
36 | * Presented nipopow proof is starting with different genesis block
37 | */
38 | case object WrongGenesis extends NipopowProofVerificationResult
39 |
40 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/mining/CandidateUtils.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.mining
2 |
3 | import org.ergoplatform.mining.AutolykosPowScheme.derivedHeaderFields
4 | import org.ergoplatform.modifiers.history.{ADProofs, BlockTransactions}
5 | import org.ergoplatform.modifiers.history.header.HeaderWithoutPow
6 | import scorex.crypto.hash.Digest32
7 |
8 | /**
9 | * Functions related to block candidate generation
10 | */
11 | object CandidateUtils {
12 | /**
13 | * Derives header without pow from [[CandidateBlock]].
14 | */
15 | def deriveUnprovenHeader(candidate: CandidateBlock): HeaderWithoutPow = {
16 | val (parentId, height) = derivedHeaderFields(candidate.parentOpt)
17 | val transactionsRoot =
18 | BlockTransactions.transactionsRoot(candidate.transactions, candidate.version)
19 | val adProofsRoot = ADProofs.proofDigest(candidate.adProofBytes)
20 | val extensionRoot: Digest32 = candidate.extension.digest
21 |
22 | HeaderWithoutPow(
23 | candidate.version,
24 | parentId,
25 | adProofsRoot,
26 | candidate.stateRoot,
27 | transactionsRoot,
28 | candidate.timestamp,
29 | candidate.nBits,
30 | height,
31 | extensionRoot,
32 | candidate.votes,
33 | Array.emptyByteArray
34 | )
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/mining/ModQHash.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.mining
2 |
3 | import org.bouncycastle.util.BigIntegers
4 | import scorex.crypto.hash.Blake2b256
5 | import scorex.util.{ScorexEncoding, ScorexLogging}
6 |
7 | import scala.annotation.tailrec
8 |
9 | /**
10 | * One way cryptographic hash function that produces numbers in [0,q) range.
11 | * It calculates Blake2b256 hash of a provided input and checks whether the result is
12 | * in range from 0 to a maximum number divisible by q without remainder.
13 | * If yes, it returns the result mod q, otherwise make one more iteration using hash as an input.
14 | * This is done to ensure uniform distribution of the resulting numbers.
15 | *
16 | * Used in Autolykos v1 only!
17 | */
18 | class ModQHash(val q: BigInt) extends ScorexLogging with ScorexEncoding {
19 | assert(q.bigInteger.bitLength() <= 256, "We use 256 bit hash here")
20 | // biggest number <= 2^256 that is divisible by q without remainder
21 | val validRange: BigInt = (BigInt(2).pow(256) / q) * q
22 |
23 | @tailrec
24 | final def hash(input: Array[Byte]): BigInt = {
25 | val hashed = Blake2b256(input)
26 | val bi = BigInt(BigIntegers.fromUnsignedByteArray(hashed))
27 | if (bi < validRange) {
28 | bi.mod(q)
29 | } else {
30 | log.trace(s"Calculate one more hash for ${encoder.encode(input)} and q=$q")
31 | hash(hashed)
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/modifiers/BlockSection.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.modifiers
2 |
3 | import io.circe.Encoder
4 | import org.ergoplatform.modifiers.history.extension.Extension
5 | import org.ergoplatform.modifiers.history.header.Header
6 | import org.ergoplatform.modifiers.history.{ADProofs, BlockTransactions}
7 |
8 | /**
9 | * Block section, so a header, or block transactions, or extension, or ADProofs.
10 | */
11 | trait BlockSection extends ErgoNodeViewModifier {
12 | /**
13 | * Id of another block section of the same type, which should be applied to the node view before this modifier
14 | */
15 | def parentId: scorex.util.ModifierId
16 |
17 | }
18 |
19 | object BlockSection {
20 |
21 | implicit val jsonEncoder: Encoder[BlockSection] = Encoder.instance {
22 | case h: Header => Header.jsonEncoder(h)
23 | case bt: BlockTransactions => BlockTransactions.jsonEncoder(bt)
24 | case adp: ADProofs => ADProofs.jsonEncoder(adp)
25 | case ext: Extension => Extension.jsonEncoder(ext)
26 | case other => throw new Exception(s"Unknown block section type: $other")
27 | }
28 |
29 | /** Immutable empty array can be shared to avoid allocations. */
30 | val emptyArray: Array[BlockSection] = Array.empty[BlockSection]
31 | }
32 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/modifiers/NonHeaderBlockSection.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.modifiers
2 |
3 | import org.ergoplatform.settings.Algos
4 | import scorex.crypto.hash.Digest32
5 | import scorex.util.{ModifierId, bytesToId, idToBytes}
6 |
7 | /**
8 | * An interface for Ergo block section which contains corresponding header id and a digest of its payload.
9 | */
10 | trait NonHeaderBlockSection extends BlockSection {
11 |
12 | override lazy val serializedId: Array[Byte] =
13 | NonHeaderBlockSection.computeIdBytes(modifierTypeId, headerId, digest)
14 |
15 | override lazy val id: ModifierId = bytesToId(serializedId)
16 |
17 | def digest: Digest32
18 |
19 | def headerId: ModifierId
20 |
21 | override def parentId: ModifierId = headerId
22 | }
23 |
24 | object NonHeaderBlockSection {
25 | def computeId(modifierType: NetworkObjectTypeId.Value, headerId: ModifierId, digest: Array[Byte]): ModifierId =
26 | bytesToId(computeIdBytes(modifierType, headerId, digest))
27 |
28 | def computeIdBytes(modifierType: NetworkObjectTypeId.Value, headerId: ModifierId, digest: Array[Byte]): Array[Byte] =
29 | Algos.hash.prefixedHash(modifierType, idToBytes(headerId), digest)
30 | }
31 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/modifiers/TransactionsCarryingBlockSection.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.modifiers
2 |
3 | import org.ergoplatform.modifiers.mempool.ErgoTransaction
4 |
5 | /**
6 | * Block section which contains transactions
7 | */
8 | trait TransactionsCarryingBlockSection extends BlockSection {
9 |
10 | def transactions: Seq[ErgoTransaction]
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/extension/ExtensionSerializer.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.modifiers.history.extension
2 |
3 | import org.ergoplatform.settings.Constants
4 | import org.ergoplatform.serialization.ErgoSerializer
5 | import scorex.util.serialization.{Reader, Writer}
6 | import scorex.util.{bytesToId, idToBytes}
7 |
8 | import scala.annotation.nowarn
9 |
10 | object ExtensionSerializer extends ErgoSerializer[Extension] {
11 |
12 | override def serialize(obj: Extension, w: Writer): Unit = {
13 | w.putBytes(idToBytes(obj.headerId))
14 | w.putUShort(obj.fields.size)
15 | obj.fields.foreach { case (key, value) =>
16 | w.putBytes(key)
17 | w.putUByte(value.length)
18 | w.putBytes(value)
19 | }
20 | }
21 |
22 | @nowarn
23 | override def parse(r: Reader): Extension = {
24 | val startPosition = r.position
25 | val headerId = bytesToId(r.getBytes(Constants.ModifierIdSize))
26 | val fieldsSize = r.getUShort()
27 | val fieldsView = (1 to fieldsSize).toStream.map { _ =>
28 | val key = r.getBytes(Extension.FieldKeySize)
29 | val length = r.getUByte()
30 | val value = r.getBytes(length)
31 | (key, value)
32 | }
33 | val fields = fieldsView.takeWhile(_ => r.position - startPosition < Constants.MaxExtensionSizeMax)
34 | require(r.position - startPosition < Constants.MaxExtensionSizeMax)
35 | Extension(headerId, fields, Some(r.position - startPosition))
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/header/PreGenesisHeader.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.modifiers.history.header
2 |
3 | import org.ergoplatform.core.idToBytes
4 | import org.ergoplatform.nodeView.history.ErgoHistoryUtils.EmptyHistoryHeight
5 |
6 | /**
7 | * A fake header that is used to fill the chain that starts from the beginning
8 | */
9 | object PreGenesisHeader extends Header(
10 | 0.toByte,
11 | parentId = Header.GenesisParentId,
12 | ADProofsRoot = null,
13 | stateRoot = null,
14 | transactionsRoot = null,
15 | timestamp = 0L,
16 | nBits = 0L,
17 | height = EmptyHistoryHeight,
18 | extensionRoot = null,
19 | powSolution = null,
20 | votes = null,
21 | unparsedBytes = Array.emptyByteArray,
22 | sizeOpt = None) {
23 |
24 | override def serializedId: Array[Byte] = idToBytes(Header.GenesisParentId)
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/popow/PoPowParams.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.modifiers.history.popow
2 |
3 | /**
4 | * NiPoPoW proof params from the KMZ17 paper
5 | *
6 | * @param m - minimal superchain length
7 | * @param k - suffix length
8 | * @param continuous - there are two proof modes, for continuous use and one-shot use. Continuous use means
9 | * validating and adding headers is possible after the proof (which requires for headers needed
10 | * to calculate difficulty to be added to the proof). One-shot use means using the proof to just
11 | * to prove that a best chain contains some header (e.g. to work with a transaction corresponding
12 | * to the block header)
13 | *
14 | */
15 | case class PoPowParams(m: Int, k: Int, continuous: Boolean)
16 |
17 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/modifiers/mempool/UnsignedErgoTransaction.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.modifiers.mempool
2 |
3 | import org.ergoplatform._
4 |
5 |
6 | case class UnsignedErgoTransaction(override val inputs: IndexedSeq[UnsignedInput],
7 | override val dataInputs: IndexedSeq[DataInput],
8 | override val outputCandidates: IndexedSeq[ErgoBoxCandidate])
9 | extends UnsignedErgoLikeTransaction(inputs, dataInputs, outputCandidates)
10 |
11 | object UnsignedErgoTransaction {
12 | def apply(inputs: IndexedSeq[UnsignedInput],
13 | outputCandidates: IndexedSeq[ErgoBoxCandidate]): UnsignedErgoTransaction = {
14 | UnsignedErgoTransaction(inputs, IndexedSeq(), outputCandidates)
15 | }
16 |
17 | def apply(utx: UnsignedErgoLikeTransaction): UnsignedErgoTransaction =
18 | UnsignedErgoTransaction(utx.inputs, utx.dataInputs, utx.outputCandidates)
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/modifiers/state/StateChanges.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.modifiers.state
2 |
3 | import scorex.crypto.authds.avltree.batch.{Insert, Lookup, Operation, Remove}
4 |
5 |
6 | case class StateChanges(toRemove: IndexedSeq[Remove], toAppend: IndexedSeq[Insert], toLookup: IndexedSeq[Lookup]) {
7 |
8 | /**
9 | * First lookup for all leafs required by data inputs (never fails, but may return proof-of-non-existence),
10 | * then remove all leafs that should be removed,
11 | * then add new leafs
12 | */
13 | val operations: Seq[Operation] = toLookup ++ toRemove ++ toAppend
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/modifiers/transaction/Signable.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.modifiers.transaction
2 |
3 |
4 | /**
5 | * A basic trait for entities which can be signed
6 | */
7 | trait Signable {
8 |
9 | /**
10 | * Bytes to be signed
11 | */
12 | val messageToSign: Array[Byte]
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/modifiers/transaction/TooHighCostError.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.modifiers.transaction
2 |
3 | import org.ergoplatform.modifiers.mempool.ErgoTransaction
4 |
5 | /**
6 | * Exception which is indicating that transaction had too high cost during validation
7 | */
8 | case class TooHighCostError(tx: ErgoTransaction, txCost: Option[Int])
9 | extends Exception(s"Transaction $tx has too high cost ${txCost.map(_.toString).getOrElse("")}")
10 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/network/Handshake.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.network
2 |
3 | /**
4 | * Network message to be send when nodes establish a new connection.
5 | * When a node creates an outgoing connection, it will immediately advertise its Handshake.
6 | * The remote node will respond with its Handshake.
7 | * No further communication is possible until both peers have exchanged their handshakes.
8 | *
9 | * @param peerSpec - general (declared) information about peer
10 | * @param time - handshake time
11 | */
12 | case class Handshake(peerSpec: PeerSpec, time: Long)
13 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/network/HandshakeSerializer.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.network
2 |
3 | import org.ergoplatform.network.message.MessageConstants.MessageCode
4 | import org.ergoplatform.network.message.MessageSpecV1
5 | import scorex.util.serialization.{Reader, Writer}
6 |
7 | /**
8 | * The `Handshake` message provides information about the transmitting node
9 | * to the receiving node at the beginning of a connection. Until both peers
10 | * have exchanged `Handshake` messages, no other messages will be accepted.
11 | */
12 | object HandshakeSerializer extends MessageSpecV1[Handshake] {
13 | override val messageCode: MessageCode = 75: Byte
14 | override val messageName: String = "Handshake"
15 |
16 | val maxHandshakeSize: Int = 8096
17 |
18 | /**
19 | * Serializing handshake into a byte writer.
20 | *
21 | * @param hs - handshake instance
22 | * @param w - writer to write bytes to
23 | */
24 | override def serialize(hs: Handshake, w: Writer): Unit = {
25 | // first writes down handshake time, then peer specification of our node
26 | w.putULong(hs.time)
27 | PeerSpecSerializer.serialize(hs.peerSpec, w)
28 | }
29 |
30 | override def parse(r: Reader): Handshake = {
31 | require(r.remaining <= maxHandshakeSize, s"Too big handshake. Size ${r.remaining} exceeds $maxHandshakeSize limit")
32 | val time = r.getULong()
33 | val data = PeerSpecSerializer.parse(r)
34 | Handshake(data, time)
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/network/PeerFeature.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.network
2 |
3 | import org.ergoplatform.core.BytesSerializable
4 | import org.ergoplatform.serialization.ErgoSerializer
5 |
6 | /**
7 | * An abstract trait to describe peer capabilities.
8 | * During a handshake peers are sending list of their "features" to each other.
9 | * It is assumed that features are not changing when the node runs.
10 | * Maximum theoretical size of a serialized feature is 32,767 bytes.
11 | * However, handshake size limit is also to be considered
12 | * (for all the features to be sent during the handshake).
13 | */
14 | trait PeerFeature extends BytesSerializable {
15 | override type M >: this.type <: PeerFeature
16 | val featureId: PeerFeature.Id
17 | }
18 |
19 | object PeerFeature {
20 | type Id = Byte
21 | type Serializers = Map[Id, ErgoSerializer[_ <: PeerFeature]]
22 | }
23 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/network/message/InvData.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.network.message
2 |
3 | import org.ergoplatform.modifiers.NetworkObjectTypeId
4 | import scorex.util.ModifierId
5 |
6 | /**
7 | * P2P network message which is encoding "inventory", transactions or block sections the node has
8 | *
9 | * @param typeId
10 | * @param ids
11 | */
12 | case class InvData(typeId: NetworkObjectTypeId.Value, ids: Seq[ModifierId])
13 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/network/message/MessageBase.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.network.message
2 |
3 | import org.ergoplatform.network.message.MessageConstants._
4 |
5 | import scala.util.{Success, Try}
6 |
7 | /**
8 | * Trait for a ergo network message
9 | *
10 | * @param spec - message specification
11 | * @param input - message being wrapped, whether in byte-array form (if from outside),
12 | * or structured data (if formed locally)
13 | * @tparam Content - message data type
14 | */
15 | trait MessageBase[Content] {
16 | val spec: MessageSpec[Content]
17 | val input: Either[Array[Byte], Content]
18 |
19 | /**
20 | * Message data bytes
21 | */
22 | lazy val dataBytes: Array[Byte] = input match {
23 | case Left(db) => db
24 | case Right(d) => spec.toBytes(d)
25 | }
26 |
27 | /**
28 | * Structured message content
29 | */
30 | lazy val data: Try[Content] = input match {
31 | case Left(db) => spec.parseBytesTry(db)
32 | case Right(d) => Success(d)
33 | }
34 |
35 | lazy val dataLength: Int = dataBytes.length
36 |
37 | /**
38 | * @return serialized message length in bytes
39 | */
40 | def messageLength: Int = {
41 | if (dataLength > 0) {
42 | HeaderLength + ChecksumLength + dataLength
43 | } else {
44 | HeaderLength
45 | }
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/network/message/MessageConstants.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.network.message
2 |
3 | /**
4 | * Type aliases and constants related to P2P network messages formats
5 | */
6 | object MessageConstants {
7 | type MessageCode = Byte
8 |
9 | val MagicLength: Int = 4
10 |
11 | val ChecksumLength: Int = 4
12 |
13 | val HeaderLength: Int = MagicLength + 5
14 | }
15 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/network/message/MessageSpec.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.network.message
2 |
3 | import org.ergoplatform.network.Version
4 | import org.ergoplatform.network.message.MessageConstants._
5 | import org.ergoplatform.serialization.ErgoSerializer
6 |
7 | /**
8 | * Base trait for app p2p messages in the network
9 | */
10 | trait MessageSpec[Content] extends ErgoSerializer[Content] {
11 |
12 | /**
13 | * The p2p protocol version in which this message type first appeared
14 | */
15 | val protocolVersion: Version
16 |
17 | /**
18 | * Code which identifies what message type is contained in the payload
19 | */
20 |
21 | val messageCode: MessageCode
22 |
23 | /**
24 | * Name of this message type. For debug purposes only.
25 | */
26 | val messageName: String
27 |
28 | override def toString: String = s"MessageSpec($messageCode: $messageName)"
29 | }
30 |
31 | /**
32 | * P2p messages, that where implemented since the beginning.
33 | */
34 | trait MessageSpecV1[Content] extends MessageSpec[Content] {
35 |
36 | override val protocolVersion: Version = Version.initial
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/network/message/ModifiersData.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.network.message
2 |
3 | import org.ergoplatform.modifiers.NetworkObjectTypeId
4 | import scorex.util.ModifierId
5 |
6 | /**
7 | * Wrapper for block sections of the same type. Used to send multiple block sections at once ove the wire.
8 | */
9 | case class ModifiersData(typeId: NetworkObjectTypeId.Value, modifiers: Map[ModifierId, Array[Byte]])
10 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/network/message/NipopowProofData.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.network.message
2 |
3 | import org.ergoplatform.settings.Algos
4 | import scorex.util.ModifierId
5 |
6 | case class NipopowProofData(m: Int, k: Int, headerId: Option[ModifierId]) {
7 | def headerIdBytesOpt: Option[Array[Byte]] = headerId.map(Algos.decode).flatMap(_.toOption)
8 | }
9 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/network/message/NipopowProofSpec.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.network.message
2 |
3 | import scorex.util.Extensions._
4 | import scorex.util.serialization.{Reader, Writer}
5 |
6 | /**
7 | * The `NipopowProof` message is a reply to a `GetNipopowProof` message.
8 | */
9 | object NipopowProofSpec extends MessageSpecV1[Array[Byte]] {
10 |
11 | val SizeLimit = 2000000
12 | override val messageCode: Byte = 91
13 | override val messageName: String = "NipopowProof"
14 |
15 | override def serialize(proof: Array[Byte], w: Writer): Unit = {
16 | w.putUInt(proof.length)
17 | w.putBytes(proof)
18 | w.putUShort(0) // to allow adding new data in future, we are adding possible pad length
19 | }
20 |
21 | override def parse(r: Reader): Array[Byte] = {
22 | require(r.remaining <= SizeLimit, s"Too big NipopowProofSpec message(size: ${r.remaining})")
23 | val proofSize = r.getUInt().toIntExact
24 | require(proofSize > 0 && proofSize < SizeLimit)
25 | val proof = r.getBytes(proofSize)
26 | val remainingBytes = r.getUShort()
27 | if (remainingBytes > 0 && remainingBytes < SizeLimit) {
28 | r.getBytes(remainingBytes) // current version of reader just skips possible additional bytes
29 | }
30 | proof
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/network/message/RequestModifierSpec.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.network.message
2 |
3 | import org.ergoplatform.network.message.MessageConstants.MessageCode
4 | import scorex.util.serialization.{Reader, Writer}
5 |
6 | /**
7 | * The `RequestModifier` message requests one or more modifiers from another node.
8 | * The objects are requested by an inventory, which the requesting node
9 | * typically received previously by way of an `Inv` message.
10 | *
11 | * This message cannot be used to request arbitrary data, such as historic transactions no
12 | * longer in the memory pool. Full nodes may not even be able to provide older blocks if
13 | * they’ve pruned old transactions from their block database.
14 | * For this reason, the `RequestModifier` message should usually only be used to request
15 | * data from a node which previously advertised it had that data by sending an `Inv` message.
16 | *
17 | */
18 | object RequestModifierSpec extends MessageSpecV1[InvData] {
19 | override val messageCode: MessageCode = 22: Byte
20 | override val messageName: String = "RequestModifier"
21 |
22 | override def serialize(data: InvData, w: Writer): Unit = {
23 | InvSpec.serialize(data, w)
24 | }
25 |
26 | override def parse(r: Reader): InvData = {
27 | InvSpec.parse(r)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/network/message/SyncInfoMessageSpec.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.network.message
2 |
3 | import org.ergoplatform.consensus.SyncInfo
4 | import org.ergoplatform.network.message.MessageConstants.MessageCode
5 | import org.ergoplatform.serialization.ErgoSerializer
6 | import scorex.util.serialization.{Reader, Writer}
7 |
8 | /**
9 | * The `SyncInfo` message requests an `Inv` message that provides modifier ids
10 | * required be sender to synchronize his blockchain with the recipient.
11 | * It allows a peer which has been disconnected or started for the first
12 | * time to get the data it needs to request the blocks it hasn't seen.
13 | *
14 | * Payload of this message should be determined in underlying applications.
15 | */
16 | class SyncInfoMessageSpec[SI <: SyncInfo](serializer: ErgoSerializer[SI]) extends MessageSpecV1[SI] {
17 |
18 | override val messageCode: MessageCode = 65: Byte
19 | override val messageName: String = "Sync"
20 |
21 | override def serialize(data: SI, w: Writer): Unit = serializer.serialize(data, w)
22 |
23 | override def parse(r: Reader): SI = serializer.parse(r)
24 | }
25 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/network/peer/LocalAddressPeerFeature.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.network.peer
2 |
3 | import org.ergoplatform.network.PeerFeature
4 |
5 | import java.net.{InetAddress, InetSocketAddress}
6 | import org.ergoplatform.settings.PeerFeatureDescriptors
7 | import org.ergoplatform.serialization.ErgoSerializer
8 | import scorex.util.serialization._
9 | import scorex.util.Extensions._
10 |
11 | /**
12 | * Peer feature required for handling connections from/to local or loopback address
13 | * @param address local or loopback address
14 | */
15 | case class LocalAddressPeerFeature(address: InetSocketAddress) extends PeerFeature {
16 | override type M = LocalAddressPeerFeature
17 | override val featureId: PeerFeature.Id = PeerFeatureDescriptors.LocalAddressPeerFeatureId
18 |
19 | override def serializer: LocalAddressPeerFeatureSerializer.type = LocalAddressPeerFeatureSerializer
20 | }
21 |
22 | object LocalAddressPeerFeatureSerializer extends ErgoSerializer[LocalAddressPeerFeature] {
23 |
24 | private val AddressLength = 4
25 |
26 | override def serialize(obj: LocalAddressPeerFeature, w: Writer): Unit = {
27 | w.putBytes(obj.address.getAddress.getAddress)
28 | w.putUInt(obj.address.getPort.toLong)
29 | }
30 |
31 | override def parse(r: Reader): LocalAddressPeerFeature = {
32 | val fa = r.getBytes(AddressLength)
33 | val port = r.getUInt().toIntExact
34 | LocalAddressPeerFeature(new InetSocketAddress(InetAddress.getByAddress(fa), port))
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/nodeView/ErgoContext.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView
2 |
3 | import org.ergoplatform.ErgoLikeContext
4 | import org.ergoplatform.nodeView.state.ErgoStateContext
5 | import org.ergoplatform.sdk.wallet.protocol.context.TransactionContext
6 | import org.ergoplatform.wallet.interpreter.ErgoInterpreter
7 | import org.ergoplatform.wallet.protocol.context.InputContext
8 |
9 | /**
10 | * Context to be used during transaction verification
11 | */
12 | class ErgoContext(val stateContext: ErgoStateContext,
13 | transactionContext: TransactionContext,
14 | inputContext: InputContext,
15 | override val costLimit: Long,
16 | override val initCost: Long)
17 | extends ErgoLikeContext(ErgoInterpreter.avlTreeFromDigest(stateContext.previousStateDigest),
18 | stateContext.sigmaLastHeaders,
19 | stateContext.sigmaPreHeader,
20 | transactionContext.dataBoxes,
21 | transactionContext.boxesToSpend,
22 | transactionContext.spendingTransaction,
23 | inputContext.selfIndex.toInt,
24 | inputContext.extension,
25 | stateContext.validationSettings.sigmaSettings,
26 | costLimit,
27 | initCost,
28 | activatedScriptVersion = (stateContext.blockVersion - 1).toByte // block version N of ErgoProtocol corresponds to version N-1 of ErgoTree (aka script version)
29 | )
30 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/nodeView/LocallyGeneratedModifier.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView
2 |
3 | import org.ergoplatform.modifiers.BlockSection
4 |
5 | /**
6 | * Wrapper for locally generated block section
7 | */
8 | case class LocallyGeneratedModifier(pmod: BlockSection)
9 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryUtils.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.history
2 |
3 | import org.ergoplatform.ErgoLikeContext
4 | import org.ergoplatform.modifiers.history.header.Header
5 |
6 | /**
7 | * Repository for types, constants and functions related to blockchain database (ErgoHistory)
8 | */
9 | object ErgoHistoryUtils {
10 | /**
11 | * Type for time, represents machine-specific timestamp of a transaction
12 | * or block section, as milliseconds passed since beginning of UNIX
13 | * epoch on the machine
14 | */
15 | type Time = Long
16 |
17 | type Height = ErgoLikeContext.Height // Int
18 | type Score = BigInt
19 | type Difficulty = BigInt
20 | type NBits = Long
21 |
22 | val CharsetName = "UTF-8"
23 |
24 | val EmptyHistoryHeight: Int = 0
25 | val GenesisHeight: Int = EmptyHistoryHeight + 1 // first block has height == 1
26 | /**
27 | * Minimal superchain length ('m' in KMZ17 paper) value used in NiPoPoW proofs for bootstrapping
28 | */
29 | val P2PNipopowProofM = 6
30 |
31 | /**
32 | * Suffix length ('k' in KMZ17 paper) value used in NiPoPoW proofs for bootstrapping
33 | */
34 | val P2PNipopowProofK = 10
35 | def heightOf(headerOpt: Option[Header]): Int = headerOpt.map(_.height).getOrElse(EmptyHistoryHeight)
36 | }
37 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/nodeView/mempool/TransactionMembershipProof.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.mempool
2 |
3 | import io.circe.{Encoder, Json}
4 | import org.ergoplatform.sdk.JsonCodecs
5 | import org.ergoplatform.settings.Algos
6 | import scorex.crypto.authds.merkle.MerkleProof
7 | import scorex.crypto.hash.Digest32
8 | import scorex.util.ModifierId
9 | import io.circe.syntax._
10 |
11 | /**
12 | * Container for Merkle proof for a transaction. This proof is to be checked against externally provided expected
13 | * Merkle tree digest (from a block header).
14 | *
15 | * @param txId - transaction identifier
16 | * @param proof - Merkle proof of transaction membership
17 | */
18 | case class TransactionMembershipProof(txId: ModifierId, proof: MerkleProof[Digest32])
19 |
20 | object TransactionMembershipProof extends JsonCodecs {
21 |
22 | implicit val merkleProofEncoder: Encoder[MerkleProof[Digest32]] = Encoder.instance { mp: MerkleProof[Digest32] =>
23 | Json.obj(
24 | "leaf" -> Algos.encode(mp.leafData).asJson,
25 | "levels" -> mp.levels.map{case (digest, side) => Algos.encode(side +: digest)}.asJson
26 | )
27 | }
28 |
29 | implicit val txMembershipProofEncoder: Encoder[TransactionMembershipProof] = Encoder.instance { tmp: TransactionMembershipProof =>
30 | tmp.proof.asJson
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/nodeView/state/StateType.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.state
2 |
3 | import org.ergoplatform.nodeView.state.StateType.StateTypeCode
4 |
5 |
6 | sealed trait StateType {
7 | def stateTypeCode: StateTypeCode
8 |
9 | def stateTypeName: String
10 | def requireProofs: Boolean
11 |
12 | /**
13 | * @return whether UTXO set is fully stored in a mode
14 | */
15 | def holdsUtxoSet: Boolean = !requireProofs
16 |
17 | override def toString: String = stateTypeName
18 | }
19 |
20 | object StateType {
21 | type StateTypeCode = Byte
22 |
23 | case object Utxo extends StateType {
24 | override val stateTypeCode: StateTypeCode = 0: Byte
25 | override val stateTypeName: String = "utxo"
26 | override val requireProofs: Boolean = false
27 | }
28 |
29 | case object Digest extends StateType {
30 | override val stateTypeCode: StateTypeCode = 1: Byte
31 | override val stateTypeName: String = "digest"
32 | override val requireProofs: Boolean = true
33 | }
34 |
35 | val values: Seq[StateType] = Seq(Utxo, Digest)
36 |
37 | def fromCode(code: StateTypeCode): StateType = if (code == Utxo.stateTypeCode) {
38 | Utxo
39 | } else if (code == Digest.stateTypeCode) {
40 | Digest
41 | } else {
42 | throw new Exception(s"Unkown state type code $code")
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/nodeView/state/VotingData.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.state
2 |
3 | import org.ergoplatform.serialization.ErgoSerializer
4 | import scorex.util.serialization.{Reader, Writer}
5 | import scorex.util.Extensions._
6 |
7 | case class VotingData(epochVotes: Array[(Byte, Int)]) {
8 |
9 | def update(voteFor: Byte): VotingData = {
10 | this.copy(epochVotes = epochVotes.map { case (id, votes) =>
11 | if (id == voteFor) id -> (votes + 1) else id -> votes
12 | })
13 | }
14 |
15 | override def canEqual(that: Any): Boolean = that.isInstanceOf[VotingData]
16 |
17 | override def equals(obj: scala.Any): Boolean = obj match {
18 | case v: VotingData => v.epochVotes.sameElements(this.epochVotes)
19 | case _ => false
20 | }
21 |
22 | }
23 |
24 | object VotingData {
25 | val empty = VotingData(Array.empty)
26 | }
27 |
28 | object VotingDataSerializer extends ErgoSerializer[VotingData] {
29 |
30 | override def serialize(obj: VotingData, w: Writer): Unit = {
31 | w.putUShort(obj.epochVotes.length)
32 | obj.epochVotes.foreach { case (id, cnt) =>
33 | w.put(id)
34 | w.putUInt(cnt.toLong)
35 | }
36 | }
37 |
38 | override def parse(r: Reader): VotingData = {
39 | val votesCount = r.getUShort()
40 | val epochVotes = (0 until votesCount).map {_ =>
41 | r.getByte() -> r.getUInt().toIntExact
42 | }
43 | VotingData(epochVotes.toArray)
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/settings/ChainSettingsReader.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.settings
2 |
3 | import com.typesafe.config.ConfigFactory
4 | import net.ceedubs.ficus.Ficus._
5 | import net.ceedubs.ficus.readers.ArbitraryTypeReader._
6 | import java.io.File
7 |
8 | object ChainSettingsReader extends PowSchemeReaders with ModifierIdReader with SettingsReaders{
9 | def read(path: String): Option[ChainSettings] = {
10 | val file = new File(path)
11 | if (file.exists) {
12 | val cfg = ConfigFactory.parseFile(file)
13 | val fallback = ConfigFactory.parseFile(new File("src/main/resources/application.conf"))
14 | val network = ConfigFactory.parseFile(new File("src/main/resources/testnet.conf"))
15 | val fullConfig = ConfigFactory
16 | .defaultOverrides()
17 | .withFallback(cfg) //order
18 | .withFallback(network) //matters
19 | .withFallback(fallback) //here
20 | .resolve()
21 |
22 | val chainSettings = fullConfig.as[ChainSettings]("ergo.chain")
23 | Some(chainSettings)
24 | } else None
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/settings/ClientCapabilities.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.settings
2 |
3 | import org.ergoplatform.nodeView.state.StateType
4 |
5 | /**
6 | * Features client may have enabled, they are reported to other peers
7 | */
8 | trait ClientCapabilities {
9 | val stateType: StateType
10 | val verifyTransactions: Boolean
11 | val blocksToKeep: Int
12 | val utxoSettings: UtxoSettings
13 | val nipopowSettings: NipopowSettings
14 | }
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/settings/LaunchParameters.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.settings
2 |
3 | /**
4 | * Parameters corresponding to initial moment of time in the mainnet and the testnet
5 | */
6 | object LaunchParameters extends Parameters(height = 0,
7 | parametersTable = Parameters.DefaultParameters,
8 | proposedUpdate = ErgoValidationSettingsUpdate.empty)
9 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/settings/ModifierIdReader.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.settings
2 |
3 | import com.typesafe.config.Config
4 | import net.ceedubs.ficus.readers.ValueReader
5 | import scorex.util.ModifierId
6 |
7 | trait ModifierIdReader {
8 |
9 | implicit val modifierIdReader: ValueReader[ModifierId] = new ValueReader[ModifierId] {
10 | override def read(cfg: Config, path: String): ModifierId = {
11 | ModifierId @@ cfg.getString(path)
12 | }
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/settings/NipopowSettings.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.settings
2 |
3 | import com.typesafe.config.Config
4 | import net.ceedubs.ficus.Ficus._
5 | import net.ceedubs.ficus.readers.ValueReader
6 |
7 | /**
8 | * Settings related to headers-chain bootstrapping with NiPoPoWs. See ergo.node.nipopow section for settings description.
9 | */
10 | case class NipopowSettings(nipopowBootstrap: Boolean, p2pNipopows: Int)
11 |
12 | /**
13 | * Custom settings reader for `NipopowSettings`
14 | */
15 | trait NipopowSettingsReader {
16 | implicit val nipopowSettingsReader: ValueReader[NipopowSettings] =
17 | new ValueReader[NipopowSettings]
18 | { def read(cfg: Config, path: String) =
19 | NipopowSettings(
20 | cfg.as[Boolean](s"$path.nipopowBootstrap"),
21 | cfg.as[Int](s"$path.p2pNipopows")
22 | )
23 | }
24 | }
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/settings/PeerFeatureDescriptors.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.settings
2 |
3 | import org.ergoplatform.network.{ModeFeatureSerializer, PeerFeature}
4 | import org.ergoplatform.network.peer.{LocalAddressPeerFeatureSerializer, RestApiUrlPeerFeatureSerializer, SessionIdPeerFeatureSerializer}
5 |
6 | /**
7 | * Repository of existing peer feature identifiers, stores their ids along with serializers
8 | */
9 | object PeerFeatureDescriptors {
10 | /**
11 | * See `LocalAddressPeerFeature`
12 | */
13 | val LocalAddressPeerFeatureId: Byte = 2: Byte
14 |
15 | /**
16 | * See `SessionIdPeerFeature`
17 | */
18 | val SessionIdPeerFeatureId: Byte = 3: Byte
19 |
20 | /**
21 | * See `RestApiUrlPeerFeature`
22 | */
23 | val RestApiUrlFeatureId: Byte = 4: Byte
24 |
25 | /**
26 | * See `ModePeerFeature`
27 | */
28 | val ModeFeatureId: Byte = 16: Byte
29 |
30 | /**
31 | * All the peer feature serializers should be here
32 | */
33 | val FeatureSerializers: PeerFeature.Serializers = Map(
34 | LocalAddressPeerFeatureId -> LocalAddressPeerFeatureSerializer,
35 | SessionIdPeerFeatureId -> SessionIdPeerFeatureSerializer,
36 | RestApiUrlFeatureId -> RestApiUrlPeerFeatureSerializer,
37 | ModeFeatureId -> ModeFeatureSerializer
38 | )
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/settings/PowSchemeReaders.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.settings
2 |
3 | import com.typesafe.config.{Config, ConfigException}
4 | import net.ceedubs.ficus.Ficus._
5 | import net.ceedubs.ficus.readers.ValueReader
6 | import org.ergoplatform.mining._
7 |
8 | trait PowSchemeReaders {
9 |
10 | implicit val powSchemeReader: ValueReader[AutolykosPowScheme] = new ValueReader[AutolykosPowScheme] {
11 | override def read(cfg: Config, path: String): AutolykosPowScheme = {
12 | val schemeNameKey = s"$path.powType"
13 | val schemeName = cfg.getString(schemeNameKey)
14 | val n = cfg.as[Int](s"$path.n")
15 | val k = cfg.as[Int](s"$path.k")
16 | if (schemeName == "autolykos") {
17 | new AutolykosPowScheme(k, n)
18 | } else if (schemeName == "fake") {
19 | new DefaultFakePowScheme(k, n)
20 | } else {
21 | throw new ConfigException.BadValue(schemeNameKey, schemeName)
22 | }
23 | }
24 | }
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/settings/ReemissionSettings.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.settings
2 |
3 | import org.ergoplatform.ErgoBox
4 | import org.ergoplatform.reemission.ReemissionRules
5 | import org.ergoplatform.wallet.boxes.ErgoBoxSerializer
6 | import scorex.util.ModifierId
7 | import scorex.util.encode.Base16
8 | import sigmastate.utils.Extensions.ModifierIdOps
9 | import sigma.Coll
10 | /**
11 | * Configuration section for re-emission (EIP27) parameters
12 | *
13 | * @see src/main/resources/application.conf for parameters description
14 | */
15 | case class ReemissionSettings(checkReemissionRules: Boolean,
16 | emissionNftId: ModifierId,
17 | reemissionTokenId: ModifierId,
18 | reemissionNftId: ModifierId,
19 | activationHeight: Int,
20 | reemissionStartHeight: Int,
21 | injectionBoxBytesEncoded: String) {
22 |
23 | val emissionNftIdBytes: Coll[Byte] = emissionNftId.toColl
24 | val reemissionNftIdBytes: Coll[Byte] = reemissionNftId.toColl
25 | val reemissionTokenIdBytes: Coll[Byte] = reemissionTokenId.toColl
26 |
27 | lazy val InjectionBoxBytes: Array[Byte] = Base16.decode(injectionBoxBytesEncoded).get
28 | lazy val injectionBox: ErgoBox = ErgoBoxSerializer.parseBytes(InjectionBoxBytes)
29 |
30 | val reemissionRules = new ReemissionRules(reemissionSettings = this)
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/settings/SettingsReaders.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.settings
2 |
3 | import com.typesafe.config.Config
4 | import net.ceedubs.ficus.readers.ValueReader
5 |
6 | import java.io.File
7 | import java.net.InetSocketAddress
8 |
9 | trait SettingsReaders {
10 | implicit val fileReader: ValueReader[File] = {
11 | new ValueReader[File] {
12 | def read(cfg: Config, path: String) = new File(cfg.getString(path))
13 | }
14 | }
15 |
16 | implicit val byteValueReader: ValueReader[Byte] = {
17 | new ValueReader[Byte] {
18 | def read(cfg: Config, path: String) = cfg.getInt(path).toByte
19 | }
20 | }
21 |
22 | implicit val inetSocketAddressReader: ValueReader[InetSocketAddress] = {
23 | new ValueReader[InetSocketAddress] {
24 | def read(config: Config, path: String) = {
25 | val string = config.getString(path)
26 | val index = string.lastIndexOf(':')
27 | new InetSocketAddress(string.substring(0, index), string.substring(index + 1).toInt)
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/settings/UtxoSettings.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.settings
2 |
3 | import com.typesafe.config.Config
4 | import net.ceedubs.ficus.Ficus._
5 | import net.ceedubs.ficus.readers.ValueReader
6 |
7 | /**
8 | * Settings related to state bootstrapping with UTXO set snapshots. See ergo.node.utxo section for settings description.
9 | */
10 | case class UtxoSettings(utxoBootstrap: Boolean, storingUtxoSnapshots: Int, p2pUtxoSnapshots: Int)
11 |
12 | /**
13 | * Custom settings reader for `UtxoSettings`
14 | */
15 | trait UtxoSettingsReader {
16 | implicit val utxoSettingsReader: ValueReader[UtxoSettings] = {
17 | new ValueReader[UtxoSettings] {
18 | def read(cfg: Config, path: String) = UtxoSettings(
19 | cfg.as[Boolean](s"$path.utxoBootstrap"),
20 | cfg.as[Int](s"$path.storingUtxoSnapshots"),
21 | cfg.as[Int](s"$path.p2pUtxoSnapshots")
22 | )
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/settings/VotingSettings.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.settings
2 |
3 | case class VotingSettings(votingLength: Int,
4 | softForkEpochs: Int,
5 | activationEpochs: Int,
6 | version2ActivationHeight: Int,
7 | version2ActivationDifficultyHex: String) {
8 |
9 | def softForkApproved(votesCount: Int): Boolean = votesCount > votingLength * softForkEpochs * 9 / 10
10 |
11 | def changeApproved(count: Int): Boolean = count > votingLength / 2
12 | }
13 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/utils/LoggingUtil.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.utils
2 |
3 | object LoggingUtil {
4 |
5 | def getReasonMsg(t: Throwable): String = Option(t.getMessage)
6 | .map(m => s"${t.getClass.getName}: $m")
7 | .getOrElse(t.getClass.getName)
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/utils/ScorexEncoding.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.utils
2 |
3 | /**
4 | * Trait with bytes to string encoder
5 | * TODO extract to ScorexUtils project
6 | */
7 | trait ScorexEncoding {
8 | implicit val encoder: ScorexEncoder = ScorexEncoder.default
9 | }
10 |
--------------------------------------------------------------------------------
/ergo-core/src/main/scala/org/ergoplatform/validation/ValidationSettings.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.validation
2 |
3 | import org.ergoplatform.modifiers.NetworkObjectTypeId
4 | import org.ergoplatform.validation.ValidationResult.Invalid
5 | import scorex.util.ModifierId
6 |
7 | /**
8 | * Specifies the strategy to by used (fail-fast or error-accumulative), a set of
9 | * activated validation rules with corresponding error messages
10 | */
11 | abstract class ValidationSettings {
12 | val isFailFast: Boolean
13 |
14 | /**
15 | * @return validation error of type `id` for block section `modifierId` of type `modifierTypeId`, error details in `e`
16 | */
17 | def getError(id: Short, e: Throwable, modifierId: ModifierId, modifierTypeId: NetworkObjectTypeId.Value): Invalid =
18 | getError(id, InvalidModifier(e.getMessage, modifierId, modifierTypeId))
19 |
20 | def getError(id: Short, invalidMod: InvalidModifier): ValidationResult.Invalid
21 |
22 | def isActive(id: Short): Boolean
23 | }
24 |
--------------------------------------------------------------------------------
/ergo-core/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | System.out
6 |
7 | WARN
8 |
9 |
10 | [%thread] >> [%-5level] %logger{36} >> %d{HH:mm:ss.SSS} %msg%n
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/ergo-core/src/test/resources/settings.conf:
--------------------------------------------------------------------------------
1 | {
2 | "ergo": {
3 | "node": {
4 | "blocksToKeep": 13,
5 | "mempoolSorting": "bySize"
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/ergo-core/src/test/resources/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "ergo": {
3 | "node": {
4 | "blocksToKeep": 12,
5 | "mempoolSorting": "bySize"
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/ergo-core/src/test/scala/org/ergoplatform/modifiers/history/BlockTransactionsSpec.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.modifiers.history
2 |
3 | import org.ergoplatform.utils.ErgoCorePropertyTest
4 |
5 | class BlockTransactionsSpec extends ErgoCorePropertyTest {
6 | import org.ergoplatform.utils.generators.CoreObjectGenerators._
7 | import org.ergoplatform.utils.generators.ErgoCoreTransactionGenerators._
8 |
9 | property("Correct Merkle proofs are generated") {
10 | forAll(invalidBlockTransactionsGen, modifierIdGen){ case (bt, absentTx) =>
11 | // for all the transactions presented in a BlockTransactions instance valid proofs should be generated
12 | bt.transactions.forall{t => BlockTransactions.proofValid(bt.digest, bt.proofFor(t.id).get)} shouldBe true
13 |
14 | // no proof should be generated for a transaction which is not there
15 | bt.proofFor(absentTx).isDefined shouldBe false
16 | }
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/ergo-core/src/test/scala/org/ergoplatform/modifiers/history/PoPowHeaderSpec.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.modifiers.history
2 |
3 | import org.ergoplatform.modifiers.history.popow.NipopowAlgos
4 | import org.ergoplatform.modifiers.history.popow.PoPowHeader.checkInterlinksProof
5 | import org.ergoplatform.utils.ErgoCorePropertyTest
6 | import org.scalacheck.Gen
7 |
8 | class PoPowHeaderSpec extends ErgoCorePropertyTest {
9 | import org.ergoplatform.utils.generators.CoreObjectGenerators._
10 | import org.ergoplatform.utils.ErgoCoreTestConstants._
11 |
12 | property("Check interlinks proof should be true") {
13 | forAll(Gen.nonEmptyListOf(modifierIdGen)) { interlinks =>
14 | val interlinksProof = NipopowAlgos.proofForInterlinkVector(nipopowAlgos.interlinksToExtension(interlinks)).get
15 | checkInterlinksProof(interlinks, interlinksProof) shouldBe true
16 | }
17 | }
18 |
19 | property("Check invalid interlinks proof should be false") {
20 | forAll(Gen.nonEmptyListOf(modifierIdGen), Gen.nonEmptyListOf(modifierIdGen)) { (interlinks1, interlinks2) =>
21 | val interlinksProof = NipopowAlgos.proofForInterlinkVector(nipopowAlgos.interlinksToExtension(interlinks2)).get
22 | checkInterlinksProof(interlinks1, interlinksProof) shouldBe false
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/ergo-core/src/test/scala/org/ergoplatform/network/ModePeerFeatureSpecification.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.network
2 |
3 | import org.ergoplatform.utils.ErgoCorePropertyTest
4 | import scala.util.Try
5 |
6 | class ModePeerFeatureSpecification extends ErgoCorePropertyTest {
7 | import org.ergoplatform.utils.generators.ErgoCoreGenerators._
8 | //roundtrip test is provided in SerializationTests
9 |
10 | property("additional bytes can be added") {
11 | forAll(modeFeatureGen) { mf =>
12 | val bs = mf.serializer.toBytes(mf)
13 | mf.serializer.parseBytes(bs ++ Array(1: Byte, 2: Byte, 3: Byte)) shouldEqual mf
14 | }
15 | }
16 |
17 | property("serialization of too big byte array fails") {
18 | forAll(modeFeatureGen) { mf =>
19 | val bs = mf.serializer.toBytes(mf)
20 | Try(mf.serializer.parseBytes(bs ++ Array.fill(512)(0: Byte))).isFailure shouldBe true
21 | }
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/ergo-core/src/test/scala/org/ergoplatform/network/PeerSpecSerializerSpec.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.network
2 |
3 | import org.ergoplatform.utils.ErgoCorePropertyTest
4 | import scorex.util.ByteArrayBuilder
5 | import scorex.util.serialization.{VLQByteBufferReader, VLQByteBufferWriter}
6 |
7 | import java.nio.ByteBuffer
8 |
9 | class PeerSpecSerializerSpec extends ErgoCorePropertyTest {
10 | import org.ergoplatform.utils.generators.CoreObjectGenerators._
11 |
12 | property("All variants of peer spec should be serialized and deserialized successfully") {
13 | forAll(peerSpecGen) { peerSpec =>
14 | val writer = new VLQByteBufferWriter(new ByteArrayBuilder())
15 | PeerSpecSerializer.serialize(peerSpec, writer)
16 | val reader = new VLQByteBufferReader(ByteBuffer.wrap(writer.result().toBytes))
17 | val actualPeerSpec = PeerSpecSerializer.parse(reader)
18 | peerSpec shouldBe actualPeerSpec
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ergo-core/src/test/scala/org/ergoplatform/utils/ErgoCorePropertyTest.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.utils
2 |
3 | import org.scalatest.matchers.should.Matchers
4 | import org.scalatest.propspec.AnyPropSpec
5 | import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
6 |
7 | trait ErgoCorePropertyTest extends AnyPropSpec
8 | with ScalaCheckPropertyChecks
9 | with Matchers
10 | with NoShrink
11 |
--------------------------------------------------------------------------------
/ergo-core/src/test/scala/org/ergoplatform/utils/NoShrink.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.utils
2 |
3 | import org.scalacheck.Shrink
4 |
5 | trait NoShrink {
6 | protected implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty)
7 | }
8 |
--------------------------------------------------------------------------------
/ergo-core/src/test/scala/org/ergoplatform/utils/RandomLike.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.utils
2 |
3 | import scala.util.Random
4 |
5 | trait RandomLike {
6 | def nextInt(i: Int): Int
7 | def nextBoolean(): Boolean
8 | def nextLong(): Long
9 | def nextDouble(): Double
10 | }
11 |
12 | class RandomWrapper(seed: Option[Int] = None) extends RandomLike {
13 | private[this] val rnd = seed.fold(new Random)(s => new Random(s))
14 | override def nextInt(i: Int): Int = rnd.nextInt(i)
15 | override def nextBoolean(): Boolean = rnd.nextBoolean()
16 | override def nextLong(): Long = rnd.nextLong()
17 | override def nextDouble(): Double = rnd.nextDouble()
18 | }
19 |
--------------------------------------------------------------------------------
/ergo-core/src/test/scala/org/ergoplatform/utils/SerializationTests.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.utils
2 |
3 | import org.ergoplatform.serialization.ErgoSerializer
4 | import org.scalacheck.Gen
5 | import org.scalatest.Assertion
6 | import org.scalatest.matchers.should.Matchers
7 | import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
8 |
9 | trait SerializationTests extends ScalaCheckPropertyChecks with Matchers {
10 | def checkSerializationRoundtrip[A](generator: Gen[A], serializer: ErgoSerializer[A]): Assertion = {
11 | forAll(generator) { b: A =>
12 | val recovered = serializer.parseBytes(serializer.toBytes(b))
13 | serializer.toBytes(b) shouldEqual serializer.toBytes(recovered)
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ergo-wallet/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version = 1.2.8
--------------------------------------------------------------------------------
/ergo-wallet/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.0")
2 | addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "2.0")
3 | addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.8")
4 |
--------------------------------------------------------------------------------
/ergo-wallet/src/main/java/org/ergoplatform/wallet/interface4j/crypto/ErgoSignature.java:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.wallet.interface4j.crypto;
2 |
3 | import org.bouncycastle.math.ec.custom.sec.SecP256K1Point;
4 | import scala.math.BigInt;
5 | import sigma.crypto.Platform;
6 |
7 | import java.math.BigInteger;
8 |
9 | /**
10 | * A wrapper over Schnorr signature implementation in Scala.
11 | */
12 | public class ErgoSignature {
13 |
14 | /**
15 | * Signs given `msg` using given `sk`.
16 | *
17 | * @return signature bytes
18 | */
19 | public byte[] sign(byte[] msg, BigInteger sk) {
20 | return org.ergoplatform.wallet.crypto.ErgoSignature.sign(msg, BigInt.apply(sk));
21 | }
22 |
23 | /**
24 | * Checks whether a given `signature` corresponds to a given `msg` and `pk`.
25 | *
26 | * @return `true` is the signature is valid, `false` otherwise
27 | */
28 | public boolean verify(byte[] msg, byte[] signature, SecP256K1Point pk) {
29 | return org.ergoplatform.wallet.crypto.ErgoSignature.verify(msg, signature, new Platform.Ecp(pk));
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/ergo-wallet/src/main/java/org/ergoplatform/wallet/interface4j/crypto/ErgoUnsafeProver.java:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.wallet.interface4j.crypto;
2 |
3 | import org.ergoplatform.ErgoLikeTransaction;
4 | import org.ergoplatform.UnsignedErgoLikeTransaction;
5 | import scala.collection.JavaConverters;
6 | import sigmastate.crypto.DLogProtocol;
7 | import java.util.Map;
8 |
9 | /**
10 | * A wrapper over naive Ergo prover implementation.
11 | */
12 | public class ErgoUnsafeProver {
13 |
14 | /**
15 | * Signs all inputs of a given `unsignedTx`.
16 | *
17 | * @return signed transaction
18 | */
19 | public ErgoLikeTransaction prove(UnsignedErgoLikeTransaction unsignedTx, DLogProtocol.DLogProverInput sk) {
20 | return org.ergoplatform.wallet.interpreter.ErgoUnsafeProver.prove(unsignedTx, sk);
21 | }
22 |
23 | /**
24 | * Signs all inputs of a given `unsignedTx`.
25 | *
26 | * @return signed transaction
27 | */
28 | public ErgoLikeTransaction prove(UnsignedErgoLikeTransaction unsignedTx, Map sks) {
29 | // This method of JavaConverters is supported across Scala 2.11-2.13
30 | return org.ergoplatform.wallet.interpreter.ErgoUnsafeProver.prove(
31 | unsignedTx,
32 | JavaConverters.mapAsScalaMapConverter(sks).asScala());
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/ergo-wallet/src/main/scala/org/ergoplatform/wallet/Constants.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.wallet
2 |
3 | import supertagged.TaggedType
4 |
5 | object Constants {
6 | object ScanId extends TaggedType[Short]
7 | type ScanId = ScanId.Type
8 |
9 | // Identifiers for system applications of Ergo node
10 | // Ids below 9 are reserved. Ids from 11 (inclusive) are for user scans
11 |
12 | /** SimplePayments scan identifier */
13 | val PaymentsScanId: ScanId = ScanId @@ 10.toShort
14 |
15 | /** Scan which is checking mining rewards */
16 | val MiningScanId: ScanId = ScanId @@ 9.toShort
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ErgoBoxSerializer.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.wallet.boxes
2 |
3 | import org.ergoplatform.ErgoBox
4 | import org.ergoplatform.wallet.serialization.ErgoWalletSerializer
5 | import scorex.util.serialization.{Reader, Writer}
6 | import sigma.serialization.ConstantStore
7 | import sigma.serialization.{SigmaByteReader, SigmaByteWriter}
8 |
9 | object ErgoBoxSerializer extends ErgoWalletSerializer[ErgoBox] {
10 |
11 | override def serialize(box: ErgoBox, w: Writer): Unit = {
12 | val writer = new SigmaByteWriter(w, None)
13 | ErgoBox.sigmaSerializer.serialize(box, writer)
14 | }
15 |
16 | override def parse(r: Reader): ErgoBox = {
17 | val reader = new SigmaByteReader(r, new ConstantStore(), resolvePlaceholdersToConstants = false)
18 | ErgoBox.sigmaSerializer.parse(reader)
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ReemissionData.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.wallet.boxes
2 |
3 | import scorex.util.ModifierId
4 |
5 | /**
6 | * Re-emission settings which are needed in order to construct transactions
7 | * (any of them, except ones using re-emission contract (as this class does not have all the needed data to
8 | * obtain re-emission contract. However, it is possible to use re-emission contracts in apps using Ergo Wallet API
9 | * by providing re-emission contract from outside).
10 | */
11 | case class ReemissionData(reemissionNftId: ModifierId, reemissionTokenId: ModifierId)
12 |
--------------------------------------------------------------------------------
/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/TrackedBoxStatus.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.wallet.boxes
2 |
3 |
4 | sealed abstract class ChainStatus(val onChain: Boolean)
5 |
6 | object ChainStatus {
7 |
8 | /** Box is presented in blockchain (main chain only). */
9 | case object OnChain extends ChainStatus(onChain = true)
10 |
11 | /** Box isn't presented in blockchain (awaiting in mempool to be added). */
12 | case object OffChain extends ChainStatus(onChain = false)
13 |
14 | }
15 |
16 | sealed abstract class SpendingStatus(val spent: Boolean)
17 |
18 | object SpendingStatus {
19 |
20 | case object Spent extends SpendingStatus(spent = true)
21 |
22 | case object Unspent extends SpendingStatus(spent = false)
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/ergo-wallet/src/main/scala/org/ergoplatform/wallet/crypto/HmacSHA512.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.wallet.crypto
2 |
3 | import javax.crypto.Mac
4 | import javax.crypto.spec.SecretKeySpec
5 |
6 | object HmacSHA512 {
7 |
8 | private val HashAlgo = "HmacSHA512"
9 |
10 | def hash(key: Array[Byte], data: Array[Byte]): Array[Byte] = initialize(key).doFinal(data)
11 |
12 | private def initialize(byteKey: Array[Byte]) = {
13 | val hmacSha512 = Mac.getInstance(HashAlgo)
14 | val keySpec = new SecretKeySpec(byteKey, HashAlgo)
15 | hmacSha512.init(keySpec)
16 | hmacSha512
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/ergo-wallet/src/main/scala/org/ergoplatform/wallet/mnemonic/WordList.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.wallet.mnemonic
2 |
3 | import scala.io.{BufferedSource, Codec, Source}
4 | import scala.util.{Failure, Try}
5 |
6 | final case class WordList(words: Seq[String], delimiter: String)
7 |
8 | object WordList {
9 |
10 | val AvailableLanguages: Seq[String] = Seq(
11 | "chinese_simplified",
12 | "chinese_traditional",
13 | "english",
14 | "french",
15 | "italian",
16 | "japanese",
17 | "korean",
18 | "spanish"
19 | )
20 |
21 | def load(languageId: String): Try[WordList] = languageId match {
22 | case "japanese" => loadFile(resourceLoader("japanese.txt")).map(WordList(_, "\u3000"))
23 | case other if AvailableLanguages contains other => loadFile(resourceLoader(s"$other.txt")).map(WordList(_, " "))
24 | case other => Failure(new IllegalArgumentException(s"Unknown language $other"))
25 | }
26 |
27 | private def loadFile(loadFile: () => BufferedSource): Try[Seq[String]] =
28 | Try(loadFile()).map(r => try r.getLines().toList finally r.close())
29 |
30 | private def resourceLoader(fileName: String): () => BufferedSource =
31 | () => Source.fromInputStream(getClass.getResourceAsStream(s"/wordlist/$fileName"))(Codec.UTF8)
32 | }
33 |
--------------------------------------------------------------------------------
/ergo-wallet/src/main/scala/org/ergoplatform/wallet/protocol/Constants.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.wallet.protocol
2 |
3 | object Constants {
4 |
5 | val HashLength: Int = 32
6 |
7 | val BlocksPerHour = 30
8 |
9 | val BlocksPerDay: Int = BlocksPerHour * 24
10 |
11 | val BlocksPerWeek: Int = BlocksPerDay * 7
12 |
13 | val BlocksPerMonth: Int = BlocksPerDay * 30
14 |
15 | val BlocksPerYear: Int = BlocksPerDay * 365
16 |
17 | //For how many blocks a box could be put into the state with no paying storage rent.
18 | //4 years
19 | val StoragePeriod: Int = 4 * BlocksPerYear
20 |
21 | val StorageContractCost: Long = 50
22 |
23 | val StorageIndexVarId: Byte = Byte.MaxValue
24 | }
25 |
--------------------------------------------------------------------------------
/ergo-wallet/src/main/scala/org/ergoplatform/wallet/protocol/context/InputContext.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.wallet.protocol.context
2 |
3 | import sigma.interpreter.ContextExtension
4 |
5 | /**
6 | * Part of execution context regarding a box to be spent.
7 | * It includes index of the box in inputs and also context extension
8 | * (additional key-value pairs provided during the spending)
9 | *
10 | * @param selfIndex - index of the box in spending transaction inputs
11 | * @param extension - input-provided context extension
12 | */
13 | case class InputContext(selfIndex: Short, extension: ContextExtension)
14 |
--------------------------------------------------------------------------------
/ergo-wallet/src/main/scala/org/ergoplatform/wallet/serialization/ErgoWalletSerializer.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.wallet.serialization
2 |
3 | import java.nio.ByteBuffer
4 | import scorex.util.ByteArrayBuilder
5 | import scorex.util.serialization._
6 | import sigma.serialization._
7 |
8 | import scala.util.Try
9 |
10 | trait ErgoWalletSerializer[T] extends Serializer[T, T, Reader, Writer] {
11 |
12 | def toBytes(obj: T): Array[Byte] = {
13 | val writer = new VLQByteBufferWriter(new ByteArrayBuilder())
14 | serialize(obj, writer)
15 | writer.result().toBytes
16 | }
17 |
18 | def parseBytes(bytes: Array[Byte]): T = {
19 | val reader = new VLQByteBufferReader(ByteBuffer.wrap(bytes))
20 | parse(reader)
21 | }
22 |
23 | def parseBytesTry(bytes: Array[Byte]): Try[T] = Try(parseBytes(bytes))
24 |
25 | }
26 |
27 | object ErgoWalletSerializer {
28 |
29 | /** Creates a new serializer which delegates to the given [[SigmaSerializer]]. */
30 | def fromSigmaSerializer[T](ss: SigmaSerializer[T, T]): ErgoWalletSerializer[T] = new ErgoWalletSerializer[T] {
31 | override def serialize(obj: T, w: Writer): Unit = {
32 | val sw = new SigmaByteWriter(w, None)
33 | ss.serialize(obj, sw)
34 | }
35 |
36 | override def parse(r: Reader): T = {
37 | val sr = new SigmaByteReader(r, new ConstantStore(), resolvePlaceholdersToConstants = false)
38 | ss.parse(sr)
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/ergo-wallet/src/main/scala/org/ergoplatform/wallet/serialization/JsonCodecsWrapper.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.wallet.serialization
2 |
3 | import org.ergoplatform.sdk.JsonCodecs
4 |
5 | /**
6 | * JSON Codecs provided as singleton package, not trait.
7 | * Could be useful for Java applications willing to use json codecs for ergo-related objects.
8 | */
9 | object JsonCodecsWrapper extends JsonCodecs
10 |
--------------------------------------------------------------------------------
/ergo-wallet/src/main/scala/org/ergoplatform/wallet/settings/SecretStorageSettings.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.wallet.settings
2 |
3 | import org.ergoplatform.sdk.wallet.settings.EncryptionSettings
4 |
5 | final case class SecretStorageSettings(secretDir: String, encryption: EncryptionSettings)
6 |
--------------------------------------------------------------------------------
/ergo-wallet/src/main/scala/org/ergoplatform/wallet/utils/FileUtils.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.wallet.utils
2 |
3 | import java.io.File
4 | import java.nio.file.{Files, Path}
5 | import scala.collection.JavaConverters._
6 | import scala.util.Try
7 |
8 | trait FileUtils {
9 |
10 | protected val randomPrefixLength = 10
11 |
12 | /**
13 | * Perform recursive deletion of directory content.
14 | */
15 | def deleteRecursive(root: File): Unit = {
16 | if (root.exists()) {
17 | Files.walk(root.toPath).iterator().asScala.toSeq.reverse.foreach(path => Try(Files.delete(path)))
18 | }
19 | }
20 |
21 | def createTempFile: java.io.File = {
22 | val dir = createTempDir
23 | val prefix = scala.util.Random.alphanumeric.take(randomPrefixLength).mkString
24 | val suffix = scala.util.Random.alphanumeric.take(randomPrefixLength).mkString
25 | val file = java.nio.file.Files.createTempFile(dir.toPath, prefix, suffix).toFile
26 | file.deleteOnExit()
27 | file
28 | }
29 |
30 | def createTempDir: java.io.File = {
31 | val rndString = scala.util.Random.alphanumeric.take(randomPrefixLength).mkString
32 | createTempDirForPrefix(rndString)
33 | }
34 |
35 | private def createTempDirForPrefix(prefix: String): java.io.File = {
36 | val file = java.nio.file.Files.createTempDirectory(prefix).toFile
37 | file.deleteOnExit()
38 | file
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/ergo-wallet/src/test/scala/org/ergoplatform/wallet/crypto/ErgoSignatureSpec.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.wallet.crypto
2 |
3 | import org.scalatest.matchers.should.Matchers
4 | import org.scalatest.propspec.AnyPropSpec
5 | import scorex.util.Random
6 | import sigmastate.crypto.DLogProtocol.DLogProverInput
7 |
8 | class ErgoSignatureSpec extends AnyPropSpec with Matchers {
9 | import org.ergoplatform.wallet.utils.WalletGenerators._
10 | import org.ergoplatform.wallet.crypto.ErgoSignature._
11 |
12 | property("sign/verify") {
13 | val secret = DLogProverInput(genSecret.bigInteger)
14 | val pk = secret.publicImage
15 |
16 | val msg = Random.randomBytes(128)
17 |
18 | val sig = sign(msg, secret.w)
19 |
20 | verify(msg, sig, pk.value) shouldBe true
21 | }
22 |
23 | property("always produce signature of fixed length") {
24 | (1 to 2000).foreach { _ =>
25 | val sk = DLogProverInput(genSecret.bigInteger)
26 | val sig = sign(Random.randomBytes(128), sk.w)
27 | sig.length shouldBe 56
28 | }
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/ergo-wallet/src/test/scala/org/ergoplatform/wallet/utils/TestFileUtils.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.wallet.utils
2 |
3 | import java.nio.file.Path
4 |
5 | trait TestFileUtils extends FileUtils {
6 |
7 | val basePath: Path = java.nio.file.Files.createTempDirectory(s"scorex-${System.nanoTime()}")
8 |
9 | sys.addShutdownHook {
10 | deleteRecursive(basePath.toFile)
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/papers/contractual/bitcoin.odp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ergoplatform/ergo/fb0d3098ffe95a40057b42f8e68b25ae4dfa7f03/papers/contractual/bitcoin.odp
--------------------------------------------------------------------------------
/papers/contractual/bitcoin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ergoplatform/ergo/fb0d3098ffe95a40057b42f8e68b25ae4dfa7f03/papers/contractual/bitcoin.png
--------------------------------------------------------------------------------
/papers/contractual/compile.sh:
--------------------------------------------------------------------------------
1 | pdflatex main
2 | bibtex main
3 | pdflatex main
4 | pdflatex main
5 |
--------------------------------------------------------------------------------
/papers/ergopool/compile.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | command -v pdflatex && command -v bibtex
4 | if [[ "$?" != 0 ]]; then
5 | echo "Command 'pdflatex' or 'bibtex' not exist, both must be installed. For Ubuntu, try:"
6 | echo "sudo apt install texlive-latex-base texlive-binaries"
7 | echo
8 | echo "You may also need to install additional packages like fonts, etc. For Ubuntu, try:"
9 | echo "sudo apt-get install texlive-fonts-recommended latex-xcolor texlive-latex-extra cm-super"
10 | exit 1;
11 | fi
12 |
13 | pdflatex main
14 | bibtex main
15 | pdflatex main
16 | pdflatex main
17 | rm main.aux
18 | rm main.out
19 | rm main.toc
20 | rm main.log
21 |
--------------------------------------------------------------------------------
/papers/pow_analysis/compile.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | command -v pdflatex && command -v bibtex
4 | if [[ "$?" != 0 ]]; then
5 | echo "Command 'pdflatex' or 'bibtex' not exist, both must be installed. For Ubuntu, try:"
6 | echo "sudo apt install texlive-latex-base texlive-binaries"
7 | echo
8 | echo "You may also need to install additional packages like fonts, etc. For Ubuntu, try:"
9 | echo "sudo apt-get install texlive-fonts-recommended latex-xcolor texlive-latex-extra cm-super"
10 | exit 1;
11 | fi
12 |
13 | pdflatex main
14 | bibtex main
15 | pdflatex main
16 | pdflatex main
17 | rm main.aux
18 | rm main.out
19 | rm main.toc
20 | rm main.log
21 |
--------------------------------------------------------------------------------
/papers/teaser/compile.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | command -v pdflatex && command -v bibtex
4 | if [[ "$?" != 0 ]]; then
5 | echo "Command 'pdflatex' or 'bibtex' not exist, both must be installed. For Ubuntu, try:"
6 | echo "sudo apt install texlive-latex-base texlive-binaries"
7 | echo
8 | echo "You may also need to install additional packages like fonts, etc. For Ubuntu, try:"
9 | echo "sudo apt-get install texlive-fonts-recommended latex-xcolor texlive-latex-extra cm-super"
10 | exit 1;
11 | fi
12 |
13 | rm teaser.pdf
14 | pdflatex teaser.tex
15 | bibtex teaser
16 | pdflatex teaser.tex
17 | pdflatex teaser.tex
18 | rm *.aux
19 | rm *.log
20 | rm *.bbl
21 | rm *.blg
22 | rm *.out
23 |
--------------------------------------------------------------------------------
/papers/teaser/emission.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ergoplatform/ergo/fb0d3098ffe95a40057b42f8e68b25ae4dfa7f03/papers/teaser/emission.jpg
--------------------------------------------------------------------------------
/papers/whitepaper/abstract.tex:
--------------------------------------------------------------------------------
1 | \begin{abstract}
2 | We present Ergo, a new flexible blockchain protocol.
3 | Ergo is designed for developing decentralized applications with the main focus of providing an efficient, secure and easy way to implement financial contracts.
4 |
5 | To achieve this goal, Ergo includes various technical and economic improvements to existing
6 | blockchain solutions. Every coin in Ergo is protected by a program in ErgoScript, which is
7 | a powerful and protocol-friendly scripting language based on $\Sigma$-protocols.
8 | Using ErgoScript, we can encode the conditions under which coins may be used: who can spend them,
9 | when, under what external conditions, to whom, and so on.
10 |
11 | Extended support for light nodes makes Ergo friendly for end users because it allows running
12 | contracts on untrusted commodity hardware.
13 | To be usable in the long-term, Ergo follows a survivability approach -- it uses
14 | widely-researched solutions that don't result in security issues in the future,
15 | while also preventing performance degradation over time with a new economic model.
16 | Finally, Ergo has a self-amendable protocol that allows it to absorb new ideas and
17 | improve itself in the future.
18 | \end{abstract}
--------------------------------------------------------------------------------
/papers/whitepaper/compile.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | command -v pdflatex && command -v bibtex
4 | if [[ "$?" != 0 ]]; then
5 | echo "Command 'pdflatex' or 'bibtex' not exist, both must be installed. For Ubuntu, try:"
6 | echo "sudo apt install texlive-latex-base texlive-binaries texlive-latex-recommended"
7 | echo
8 | echo "You may also need to install additional packages like fonts, etc. For Ubuntu, try:"
9 | echo "sudo apt-get install texlive-fonts-recommended latex-xcolor texlive-latex-extra cm-super"
10 | exit 1;
11 | fi
12 |
13 | rm whitepaper.pdf
14 | pdflatex whitepaper.tex
15 | bibtex whitepaper
16 | pdflatex whitepaper.tex
17 | pdflatex whitepaper.tex
18 | rm *.aux
19 | rm *.log
20 | rm *.bbl
21 | rm *.blg
22 | rm *.out
23 |
--------------------------------------------------------------------------------
/papers/whitepaper/img/batching/proofSizeFromBatchSize2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ergoplatform/ergo/fb0d3098ffe95a40057b42f8e68b25ae4dfa7f03/papers/whitepaper/img/batching/proofSizeFromBatchSize2.png
--------------------------------------------------------------------------------
/papers/whitepaper/img/batching/proofSizeFromTreeSize.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ergoplatform/ergo/fb0d3098ffe95a40057b42f8e68b25ae4dfa7f03/papers/whitepaper/img/batching/proofSizeFromTreeSize.png
--------------------------------------------------------------------------------
/papers/whitepaper/img/emission.dat:
--------------------------------------------------------------------------------
1 | age (years), foundation coins, miners coins, coins total
2 | 0, 0, 0, 0
3 | 2.0, 3941997, 35478000, 39419997
4 | 2.2465753424657535, 4233594, 39852000, 44085594
5 | 2.493150684931507, 4330792, 44225998, 48556791
6 | 2.73972602739726, 4330792, 48502795, 52833588
7 | 2.9863013698630136, 4330792, 52585192, 56915985
8 | 3.232876712328767, 4330792, 56473189, 60803982
9 | 3.4794520547945207, 4330792, 60166786, 64497579
10 | 3.7260273972602738, 4330792, 63665983, 67996776
11 | 3.9726027397260273, 4330792, 66970780, 71301573
12 | 4.219178082191781, 4330792, 70081177, 74411970
13 | 4.465753424657534, 4330792, 72997174, 77327967
14 | 4.712328767123288, 4330792, 75718771, 80049564
15 | 4.958904109589041, 4330792, 78245968, 82576761
16 | 5.205479452054795, 4330792, 80578765, 84909558
17 | 5.4520547945205475, 4330792, 82717162, 87047955
18 | 5.698630136986301, 4330792, 84661159, 88991952
19 | 5.945205479452055, 4330792, 86410756, 90741549
20 | 6.191780821917808, 4330792, 87965953, 92296746
21 | 6.438356164383562, 4330792, 89326750, 93657543
22 | 6.684931506849315, 4330792, 90493147, 94823940
23 | 6.931506849315069, 4330792, 91465144, 95795937
24 | 7.178082191780822, 4330792, 92242741, 96573534
25 | 7.424657534246576, 4330792, 92825938, 97156731
26 | 7.671232876712328, 4330792, 93214735, 97545528
27 | 7.917804414003045, 4330792, 93409132, 97739925
--------------------------------------------------------------------------------
/papers/whitepaper/img/emission.gnu:
--------------------------------------------------------------------------------
1 | set terminal png size 1024,768
2 | set output "emission.png"
3 |
4 | set xrange [0:8]
5 | set yrange [0:100000000]
6 |
7 | set style line 1 \
8 | linecolor rgb '#07820f' \
9 | linetype 1 linewidth 3
10 |
11 | set style line 2 \
12 | linecolor rgb '#0008a8' \
13 | linetype 1 linewidth 3
14 |
15 | set style line 3 \
16 | linecolor rgb '#aaaaaa' \
17 | linetype 1 linewidth 3
18 |
19 |
20 | set ytics 10000000 font ",20"
21 | set xtics font ",20"
22 | set xlabel "Year since genesis block" font ",24"
23 |
24 | set ylabel "Ergo" offset -2,0,0 font ",24"
25 | set format y "%.0sM"
26 | set lmargin 12
27 | set tmargin 2
28 |
29 | set key right center
30 | set key font ",20"
31 | set key spacing 4
32 |
33 | plot "emission.dat" using 1:4 title 'Total Monetary Base' with lines linestyle 3, \
34 | "emission.dat" using 1:3 title 'To Miners' with lines linestyle 2, \
35 | "emission.dat" using 1:2 title 'To Treasury' with lines linestyle 1
36 |
37 |
--------------------------------------------------------------------------------
/papers/whitepaper/img/emission.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ergoplatform/ergo/fb0d3098ffe95a40057b42f8e68b25ae4dfa7f03/papers/whitepaper/img/emission.png
--------------------------------------------------------------------------------
/papers/whitepaper/img/proofSize.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ergoplatform/ergo/fb0d3098ffe95a40057b42f8e68b25ae4dfa7f03/papers/whitepaper/img/proofSize.png
--------------------------------------------------------------------------------
/papers/whitepaper/whitepaper.tex:
--------------------------------------------------------------------------------
1 | \def\lncs{1}
2 |
3 | \documentclass[]{article}
4 | \RequirePackage{amsmath}
5 |
6 | \usepackage{graphicx}
7 | \usepackage{amssymb}
8 | \usepackage{color}
9 | \usepackage{hyperref}
10 | \usepackage{float}
11 | \usepackage{algorithm}
12 | \usepackage{algpseudocode}
13 |
14 | \bibliographystyle{IEEEtran}
15 |
16 | \newcommand{\knote}[1]{{\textcolor{green}{Alex notes:{#1}}}}
17 | \newcommand{\dnote}[1]{{\textcolor{red}{DNotes:{#1}}}}
18 | \newcommand{\snote}[1]{{\textcolor{blue}{SNotes:{#1}}}}
19 |
20 | \newcommand{\Ergo}{Ergo}
21 | \newcommand{\Erg}{Erg}
22 | \newcommand{\nanoErg}{nanoErg}
23 | \def\Let#1#2{\State #1 $:=$ #2}
24 | \def\LetRnd#1#2{\State #1 $\gets$ #2}
25 |
26 | \begin{document}
27 | \title{Ergo: A Resilient Platform For Contractual Money}
28 | \author{Ergo Developers, https://ergoplatform.org}
29 | \date{May 14, 2019\\v1.0}
30 |
31 | \maketitle
32 |
33 | \input{abstract}
34 |
35 | \input{intro}
36 |
37 | \input{social_contract}
38 |
39 | \input{autolykos}
40 |
41 | \input{utxo}
42 |
43 | \input{survivability}
44 |
45 | \input{currency}
46 |
47 | \input{money}
48 |
49 | \bibliography{references}
50 |
51 | \end{document}
52 |
--------------------------------------------------------------------------------
/papers/yellow/compile.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | command -v pdflatex && command -v bibtex
4 | if [ "$?" != 0 ]; then
5 | echo "Command 'pdflatex' or 'bibtex' not exist, both must be installed. For Ubuntu, try:"
6 | echo "sudo apt install texlive-latex-base texlive-binaries"
7 | echo
8 | echo "You may also need to install additional packages like fonts, etc. For Ubuntu, try:"
9 | echo "sudo apt install texlive-fonts-recommended texlive-science latex-xcolor texlive-latex-extra cm-super"
10 | echo
11 | echo "You can install all the TeX additional packages (+2Gb disk space) with: sudo apt install texlive-full"
12 | exit 1;
13 | fi
14 |
15 | rm -f YellowPaper.pdf
16 | pdflatex -shell-escape YellowPaper
17 | bibtex -shell-escape YellowPaper
18 | pdflatex -shell-escape YellowPaper
19 | pdflatex -shell-escape YellowPaper
20 |
21 | if [ $(dirname $0) = "." ]; then
22 | rm -f *.aux
23 | rm -f *.log
24 | rm -f *.blg
25 | rm -f *.bbl
26 | rm -f *.out
27 | rm -f *.toc
28 | rm -fr _minted-YellowPaper/
29 | fi
30 |
--------------------------------------------------------------------------------
/papers/yellow/img/box-transition.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ergoplatform/ergo/fb0d3098ffe95a40057b42f8e68b25ae4dfa7f03/papers/yellow/img/box-transition.png
--------------------------------------------------------------------------------
/papers/yellow/img/curve_combined.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ergoplatform/ergo/fb0d3098ffe95a40057b42f8e68b25ae4dfa7f03/papers/yellow/img/curve_combined.png
--------------------------------------------------------------------------------
/papers/yellow/img/emission.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ergoplatform/ergo/fb0d3098ffe95a40057b42f8e68b25ae4dfa7f03/papers/yellow/img/emission.jpg
--------------------------------------------------------------------------------
/papers/yellow/pow/compile.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | command -v pdflatex && command -v bibtex
4 | if [ "$?" != 0 ]; then
5 | echo "Command 'pdflatex' or 'bibtex' not exist, both must be installed. For Ubuntu, try:"
6 | echo "sudo apt install texlive-latex-base texlive-binaries"
7 | echo
8 | echo "You may also need to install additional packages like fonts, etc. For Ubuntu, try:"
9 | echo "sudo apt install texlive-fonts-recommended texlive-science latex-xcolor texlive-latex-extra cm-super"
10 | echo
11 | echo "You can install all the TeX additional packages (+2Gb disk space) with: sudo apt install texlive-full"
12 | exit 1;
13 | fi
14 |
15 | rm -f ErgoPow.pdf
16 | pdflatex ErgoPow.tex
17 | bibtex ErgoPow
18 | pdflatex ErgoPow.tex
19 | pdflatex ErgoPow.tex
20 |
21 | if [ $(dirname $0) = "." ]; then
22 | rm -f *.aux
23 | rm -f *.log
24 | fi
25 |
--------------------------------------------------------------------------------
/papers/yellow/tokens.tex:
--------------------------------------------------------------------------------
1 | Token emission is incorporated with just one conservation law added, in addition to the standard one~(that is,
2 | sum of monetary values for transaction inputs should equal the corresponding sum for outputs). One output can
3 | hold tokens of multiple kinds, the maximum number of tokens per output is 4~(in addition to the main Ergo token).
4 | They are stored in the register R3 as a sequence of $\{token\_id: amount\}$ tuples. This is the only kind of data
5 | which can be stored in R3. The emission is organized as appending an item to
6 | the dictionary. To avoid collisions, appended $token\_id$ must equal
7 | $id$ of the first input of the generating transaction. The uniqueness of outputs
8 | yields the uniqueness of tokens. Obviously, only one output can contain a new asset, and a transaction
9 | may create no more than one new asset.
10 |
11 | The validation script is then
12 | \begin{eqnarray*}
13 | &\forall\,id\in \left\{ i\, | \exists\, out \in outputs : i\in out.R3.keys
14 | \right\} \nonumber\\
15 | &\left(\sum_{in\in inputs} in.R3[id] = \sum_{out\in
16 | outputs} out.R3[id] \right) \vee \left(id = inputs[0].id\right)\,.
17 | \end{eqnarray*}
18 | Here $\sum$ stands for the safe sum, which ensures non-negativeness of all the
19 | values, and the absence of integer overflows on the way. The controlled emission of the
20 | tokens may be organized by attaching the emission script to the output which contains newly generated $token\_id$.
21 |
22 |
--------------------------------------------------------------------------------
/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=1.10.7
2 |
--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | logLevel := Level.Warn
2 |
3 | addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.3.1")
4 |
5 | addSbtPlugin("se.marcuslonnberg" % "sbt-docker" % "1.11.0")
6 |
7 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.1")
8 |
9 | addDependencyTreePlugin
10 |
11 | addSbtPlugin("com.sksamuel.scapegoat" %% "sbt-scapegoat" % "1.2.12")
12 |
13 | addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.7")
14 |
15 | addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.11.1")
16 |
17 | addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.12.2")
18 |
19 | addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.3.1")
20 |
21 | addSbtPlugin("net.bzzt" % "sbt-reproducible-builds" % "0.32")
22 |
23 | addSbtPlugin("com.github.sbt" % "sbt-dynver" % "5.1.0")
24 |
25 | addSbtPlugin("org.typelevel" % "sbt-tpolecat" % "0.5.2")
26 |
--------------------------------------------------------------------------------
/src/it/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | System.out
5 |
6 | %date{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{26} - %msg%n
7 |
8 |
9 |
10 |
11 | ${ergo.it.logging.dir:-target/logs}/test.log
12 | false
13 |
14 | %date{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{26} - %msg%n
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/it/resources/mainnetTemplate.conf:
--------------------------------------------------------------------------------
1 | ergo {
2 | # Settings for node view holder regime. See papers.yellow.ModifiersProcessing.md
3 | node {
4 | # Is the node is doing mining
5 | mining = false
6 | }
7 | }
8 |
9 | scorex {
10 | network {
11 | bindAddress = "0.0.0.0:9001"
12 | agentName = "mainnet-ergo-integration-test"
13 | }
14 | restApi {
15 | bindAddress = "0.0.0.0:9051"
16 | apiKeyHash = null
17 | timeout = 20s
18 | }
19 | }
--------------------------------------------------------------------------------
/src/it/resources/nodes.conf:
--------------------------------------------------------------------------------
1 | nodes = [{
2 | scorex {
3 | network.nodeName = node01
4 | }
5 | }, {
6 | scorex {
7 | network.nodeName = node02
8 | }
9 | }, {
10 | scorex {
11 | network.nodeName = node03
12 | }
13 | }, {
14 | scorex {
15 | network.nodeName = node04
16 | }
17 | }, {
18 | scorex {
19 | network.nodeName = node05
20 | }
21 | }, {
22 | scorex {
23 | network.nodeName = node06
24 | }
25 | }, {
26 | scorex {
27 | network.nodeName = node07
28 | }
29 | }, {
30 | scorex {
31 | network.nodeName = node08
32 | }
33 | }, {
34 | scorex {
35 | network.nodeName = node09
36 | }
37 | }, {
38 | scorex {
39 | network.nodeName = node10
40 | }
41 | }]
42 |
43 | testing {
44 | integration {
45 | cleanupDocker = ${?ERGO_INTEGRATION_TESTS_CLEANUP_DOCKER} // env var (set to true/false)
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/it/resources/testnetTemplate.conf:
--------------------------------------------------------------------------------
1 | ergo {
2 | # Settings for node view holder regime. See papers.yellow.ModifiersProcessing.md
3 | node {
4 | # Is the node is doing mining
5 | mining = false
6 | }
7 | }
8 |
9 | scorex {
10 | network {
11 | bindAddress = "0.0.0.0:9001"
12 | agentName = "testnet-ergo-integration-test"
13 | }
14 | restApi {
15 | bindAddress = "0.0.0.0:9051"
16 | apiKeyHash = null
17 | }
18 | }
--------------------------------------------------------------------------------
/src/it/scala/org/ergoplatform/it/KnownNodesSpec.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.it
2 |
3 | import com.typesafe.config.Config
4 | import org.ergoplatform.it.container.{IntegrationSuite, Node}
5 | import org.scalatest.flatspec.AnyFlatSpec
6 |
7 | import scala.concurrent.Await
8 | import scala.concurrent.duration._
9 |
10 | class KnownNodesSpec extends AnyFlatSpec with IntegrationSuite {
11 |
12 | val nodeConfigs: List[Config] = nodeSeedConfigs.take(3)
13 | .map(conf => nonGeneratingPeerConfig
14 | .withFallback(conf)
15 | .withFallback(localOnlyConfig)
16 | )
17 |
18 | val nodes: List[Node] = docker.startDevNetNodes(nodeConfigs, sequentialTopologyConfig).get
19 |
20 | // All nodes should propagate sequentially, so any node knows each other
21 | it should s"The third node knows first node" in {
22 |
23 | val node03 = nodes.find(_.nodeName == "node03").value
24 | val targetPeersCount = nodes.length - 1 /* self */
25 | val result = node03.waitForPeers(targetPeersCount).map { peers =>
26 | peers.map(_.name) should contain("node01")
27 | }
28 |
29 | Await.result(result, 10.minute)
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/it/scala/org/ergoplatform/it/LongChainSyncSpec.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.it
2 |
3 | import com.typesafe.config.Config
4 | import org.ergoplatform.it.container.{IntegrationSuite, Node}
5 | import org.scalatest.flatspec.AnyFlatSpec
6 |
7 | import scala.concurrent.duration._
8 | import scala.concurrent.{Await, Future}
9 |
10 | class LongChainSyncSpec extends AnyFlatSpec with IntegrationSuite {
11 |
12 | val chainLength = 300
13 |
14 | val minerConfig: Config = shortInternalMinerPollingInterval
15 | .withFallback(nodeSeedConfigs.head)
16 | .withFallback(localOnlyConfig)
17 |
18 | val nonGeneratingConfig: Config =
19 | nonGeneratingPeerConfig.withFallback(nodeSeedConfigs(1)).withFallback(localOnlyConfig)
20 |
21 | val miner: Node = docker.startDevNetNode(minerConfig).get
22 |
23 | it should s"Long chain ($chainLength blocks) synchronization" in {
24 |
25 | val result: Future[Int] = miner
26 | .waitForHeight(chainLength)
27 | .flatMap { _ =>
28 | val follower = docker.startDevNetNode(nonGeneratingConfig).get
29 | follower.waitForHeight(chainLength)
30 | }
31 |
32 | Await.result(result, 10.minutes)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/it/scala/org/ergoplatform/it/api/NetworkNodeApi.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.it.api
2 |
3 | import java.net.InetSocketAddress
4 |
5 | import org.ergoplatform.it.network.NetworkSender
6 |
7 | import scala.concurrent.{ExecutionContext, Future}
8 |
9 | trait NetworkNodeApi {
10 |
11 | def networkAddress: String
12 | def networkPort: Int
13 | def networkNodeName: String
14 | def chainId: Char
15 | def nonce: Long = System.currentTimeMillis()
16 |
17 | def sendByNetwork(message: Array[Byte]*)(implicit ec: ExecutionContext): Future[Unit] = {
18 | val sender = new NetworkSender(chainId, networkNodeName, nonce)
19 | sender.connect(new InetSocketAddress(networkAddress, networkPort)).map { ch =>
20 | if (ch.isActive) sender.send(ch, message: _*).map(_ => sender.close()) else sender.close()
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/it/scala/org/ergoplatform/it/api/package.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.it
2 |
3 | import io.circe.{Decoder, Json}
4 | import org.asynchttpclient.Response
5 |
6 | import scala.concurrent.{ExecutionContext, Future}
7 |
8 | package object api {
9 | implicit class ResponseFutureExt(val f: Future[Response]) extends AnyVal {
10 | def as[A: Decoder](implicit ec: ExecutionContext): Future[A] = f.map(r => Json.fromString(r.getResponseBody).as[A].right.get)(ec)
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/it/scala/org/ergoplatform/it/container/ApiChecker.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.it.container
2 |
3 | case class ApiCheckerConfig(apiAddressToCheck: String, specFilePath: String, paramsFilePath: String)
4 |
5 | case class ApiChecker(containerId: String, config: ApiCheckerConfig)
6 |
--------------------------------------------------------------------------------
/src/it/scala/org/ergoplatform/it/container/IntegrationSuite.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.it.container
2 |
3 | import org.ergoplatform.utils.ErgoTestHelpers
4 | import org.scalatest.concurrent.{IntegrationPatience, ScalaFutures}
5 | import org.scalatest.matchers.should.Matchers
6 | import org.scalatest.{BeforeAndAfterAll, Suite}
7 | import scorex.util.ScorexLogging
8 |
9 | import scala.concurrent.ExecutionContext
10 | import scala.util.Random
11 |
12 | trait IntegrationSuite
13 | extends BeforeAndAfterAll
14 | with IntegrationTestConstants
15 | with ErgoTestHelpers
16 | with ScalaFutures
17 | with IntegrationPatience
18 | with Matchers
19 | with ScorexLogging { this: Suite =>
20 |
21 | implicit def executionContext: ExecutionContext = ErgoTestHelpers.defaultExecutionContext
22 |
23 | val tempDir: String = System.getenv("TMPDIR")
24 |
25 | protected val localDataDir: String = s"$tempDir/ergo-${Random.nextInt(Int.MaxValue)}"
26 |
27 | protected val docker: Docker = new Docker(tag = getClass.getSimpleName, localDataVolumeOpt = Some(localDataDir))
28 |
29 | override protected def beforeAll(): Unit = {
30 | log.debug("Starting tests")
31 | }
32 |
33 | override protected def afterAll(): Unit = docker.close()
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/it/scala/org/ergoplatform/it/container/NodeInfo.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.it.container
2 |
3 | case class NodeInfo(hostRestApiPort: Int,
4 | hostNetworkPort: Int,
5 | containerNetworkPort: Int,
6 | containerApiPort: Int,
7 | apiIpAddress: String,
8 | networkIpAddress: String,
9 | containerId: String)
10 |
--------------------------------------------------------------------------------
/src/it/scala/org/ergoplatform/it/util/util.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.it
2 |
3 | import io.netty.util.Timer
4 |
5 | import scala.concurrent.duration.{FiniteDuration, MILLISECONDS}
6 | import scala.concurrent.{ExecutionContext, Future, Promise}
7 |
8 | package object util {
9 | implicit class TimerExt(val timer: Timer) extends AnyVal {
10 | def schedule[A](f: => Future[A], delay: FiniteDuration): Future[A] = {
11 | val p = Promise[A]
12 | try {
13 | timer.newTimeout(_ => p.completeWith(f), delay.toMillis, MILLISECONDS)
14 | } catch {
15 | case t: Throwable => p.failure(t)
16 | }
17 | p.future
18 | }
19 |
20 | def retryUntil[A](f: => Future[A], cond: A => Boolean, retryInterval: FiniteDuration)
21 | (implicit ec: ExecutionContext): Future[A] =
22 | f.flatMap(v => if (cond(v)) Future.successful(v) else schedule(retryUntil(f, cond, retryInterval), retryInterval))
23 | }
24 | implicit class RichEither[A](e: Either[String, A]) {
25 | def toFuture: Future[A] = Future.fromTry(e.left.map(new Exception(_)).toTry)
26 |
27 | def get: A = e.fold(e => throw new Exception(e), identity)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/it2/scala/org/ergoplatform/it2/TestDigestStateOnMainNetSpec.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.it2
2 |
3 | import com.typesafe.config.Config
4 | import org.ergoplatform.it.api.NodeApi.NodeInfo
5 | import org.ergoplatform.it.container.{IntegrationSuite, Node}
6 | import org.scalatest.OptionValues
7 | import org.scalatest.flatspec.AnyFlatSpec
8 |
9 | import scala.async.Async
10 | import scala.concurrent.Await
11 | import scala.concurrent.duration._
12 |
13 | class TestDigestStateOnMainNetSpec
14 | extends AnyFlatSpec
15 | with IntegrationSuite
16 | with OptionValues {
17 |
18 | val nodeConfig: Config = nodeSeedConfigs.head
19 | .withFallback(digestStatePeerConfig)
20 | .withFallback(nonGeneratingPeerConfig)
21 |
22 | val node: Node = docker.startMainNetNodeYesImSure(nodeConfig).get
23 |
24 | it should "Start a stateType=digest node on mainnet and wait for a full sync" in {
25 | val result = Async.async {
26 | Async.await(node.waitFor[NodeInfo](
27 | _.info,
28 | nodeInfo => {
29 | nodeInfo.bestBlockHeightOpt.exists(nodeInfo.bestHeaderHeightOpt.contains)
30 | },
31 | 1.minute
32 | ))
33 | }
34 | Await.result(result, 4.hours)
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/it2/scala/org/ergoplatform/it2/TestDigestStateWithPruningOnMainNetSpec.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.it2
2 |
3 | import com.typesafe.config.Config
4 | import org.ergoplatform.it.api.NodeApi.NodeInfo
5 | import org.ergoplatform.it.container.{IntegrationSuite, Node}
6 | import org.scalatest.OptionValues
7 | import org.scalatest.flatspec.AnyFlatSpec
8 |
9 | import scala.async.Async
10 | import scala.concurrent.Await
11 | import scala.concurrent.duration._
12 |
13 | class TestDigestStateWithPruningOnMainNetSpec
14 | extends AnyFlatSpec
15 | with IntegrationSuite
16 | with OptionValues {
17 |
18 | val nodeConfig: Config = nodeSeedConfigs.head
19 | .withFallback(digestStatePeerConfig)
20 | .withFallback(prunedHistoryConfig(2880))
21 | .withFallback(nonGeneratingPeerConfig)
22 |
23 | val node: Node = docker.startMainNetNodeYesImSure(nodeConfig).get
24 |
25 | it should "Start a stateType=digest(with pruning) node on mainnet and wait for a full sync" in {
26 | val result = Async.async {
27 | Async.await(node.waitFor[NodeInfo](
28 | _.info,
29 | nodeInfo => {
30 | nodeInfo.bestBlockHeightOpt.exists(nodeInfo.bestHeaderHeightOpt.contains)
31 | },
32 | 1.minute
33 | ))
34 | }
35 | Await.result(result, 4.hours)
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/it2/scala/org/ergoplatform/it2/TestOnMainNetSpec.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.it2
2 |
3 | import com.typesafe.config.Config
4 | import org.ergoplatform.it.api.NodeApi.NodeInfo
5 | import org.ergoplatform.it.container.{IntegrationSuite, Node}
6 | import org.scalatest.OptionValues
7 | import org.scalatest.flatspec.AnyFlatSpec
8 |
9 | import scala.async.Async
10 | import scala.concurrent.Await
11 | import scala.concurrent.duration._
12 |
13 | class TestOnMainNetSpec
14 | extends AnyFlatSpec
15 | with IntegrationSuite
16 | with OptionValues {
17 |
18 | val nodeConfig: Config = nodeSeedConfigs.head.withFallback(nonGeneratingPeerConfig)
19 | val node: Node = docker.startMainNetNodeYesImSure(nodeConfig).get
20 |
21 | it should "Start a node on mainnet and wait for a full sync" in {
22 | val result = Async.async {
23 | Async.await(node.waitFor[NodeInfo](
24 | _.info,
25 | nodeInfo => {
26 | nodeInfo.bestBlockHeightOpt.exists(nodeInfo.bestHeaderHeightOpt.contains)
27 | },
28 | 1.minute
29 | ))
30 | }
31 | Await.result(result, 4.hours)
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/resources/node1/application.conf:
--------------------------------------------------------------------------------
1 | # Config file for local node, suitable for testing purposes
2 | ergo {
3 | # Directory to keep data
4 | directory = "/tmp/ergo/node1/data"
5 |
6 | # Settings for node view holder regime. See papers.yellow.ModifiersProcessing.md
7 | node {
8 | # Node is doing to mine own chain and will mine one block per 5 seconds until difficulty adjustment
9 | offlineGeneration = true
10 | useExternalMiner = false
11 | mining = true
12 | internalMinerPollingInterval = 5s
13 | }
14 |
15 | chain {
16 | monetary {
17 | # Reduced 1-block reward delay for faster testing
18 | minerRewardDelay = 1
19 | }
20 | # Base16 representation of genesis state roothash
21 | genesisStateDigestHex = "840ca0b8aec2d7a6c4f1589ca6070c8a5ed5924c835cdb8f816aa773b6fe1b6302"
22 | }
23 |
24 | wallet {
25 |
26 | # Mnemonic seed used in wallet for tests
27 | testMnemonic = "drill grab fiber curtain grace pudding thank cruise elder picnic eight ozone"
28 |
29 | # Number of keys to be generated for tests
30 | testKeysQty = 5
31 | }
32 |
33 | }
34 |
35 | scorex {
36 | network {
37 | bindAddress = "0.0.0.0:9001"
38 | nodeName = "ergo-node1"
39 | knownPeers = []
40 | }
41 | restApi {
42 | # API is binded for localhost
43 | bindAddress = "127.0.0.1:9051"
44 | # No protection, anyone with access to localhost may spend your coins!
45 | apiKeyHash = null
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/resources/node2/application.conf:
--------------------------------------------------------------------------------
1 | # Config file for local node, suitable for testing purposes
2 | ergo {
3 | # Directory to keep data
4 | directory = "/tmp/ergo/node2/data"
5 |
6 | # Settings for node view holder regime. See papers.yellow.ModifiersProcessing.md
7 | node {
8 | # Node is doing to mine own chain and will mine one block per 5 seconds until difficulty adjustment
9 | offlineGeneration = false
10 | mining = true
11 | useExternalMiner = false
12 | internalMinerPollingInterval = 1s
13 | }
14 |
15 | wallet {
16 |
17 | # Mnemonic seed used in wallet for tests
18 | testMnemonic = "ozone drill grab fiber curtain grace pudding thank cruise elder eight picnic"
19 |
20 | # Number of keys to be generated for tests
21 | testKeysQty = 5
22 | }
23 |
24 | chain {
25 | monetary {
26 | # Reduced 1-block reward delay for faster testing
27 | minerRewardDelay = 1
28 | }
29 | # Base16 representation of genesis state roothash
30 | genesisStateDigestHex = "840ca0b8aec2d7a6c4f1589ca6070c8a5ed5924c835cdb8f816aa773b6fe1b6302"
31 | }
32 |
33 |
34 | }
35 |
36 | scorex {
37 | network {
38 | bindAddress = "0.0.0.0:9002"
39 | nodeName = "ergo-node2"
40 | knownPeers = ["127.0.0.1:9001"]
41 | }
42 | restApi {
43 | # API is binded for localhost
44 | bindAddress = "127.0.0.1:9052"
45 | # No protection, anyone with access to localhost may spend your coins!
46 | apiKeyHash = null
47 | }
48 | }
49 |
50 |
--------------------------------------------------------------------------------
/src/main/resources/nodeTestnet/application.conf:
--------------------------------------------------------------------------------
1 | ergo {
2 | # Directory to keep data
3 | directory = "/tmp/ergo/testnet/data"
4 |
5 | node {
6 | # Is the node is doing mining
7 | mining = true
8 | }
9 |
10 | wallet {
11 | # Save used boxes (may consume additinal disk space) or delete them immediately
12 | keepSpentBoxes = true
13 |
14 | # Mnemonic seed used in wallet for ONLY FOR TESTS!
15 | testMnemonic = "ozone drill grab fiber curtain grace pudding thank cruise elder eight picnic"
16 |
17 | # Number of keys to be generated for tests
18 | testKeysQty = 10
19 | }
20 | }
21 |
22 | scorex {
23 | network {
24 | nodeName = "my-ergo-testnet-node"
25 | }
26 | restApi {
27 | # API is binded for localhost, replace to "0.0.0.0:9052" to be available from remote host
28 | bindAddress = "127.0.0.1:9052"
29 | # Base16-encoded Blake2b hash from your secret, that should be passed in headers as api_key
30 | # SET THIS TO YOUR UNIQUE HASH IF YOU DON'T WANT ANYBODY TO ACCESS YOUR WALLET!
31 | apiKeyHash = null
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/resources/panel/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ergoplatform/ergo/fb0d3098ffe95a40057b42f8e68b25ae4dfa7f03/src/main/resources/panel/favicon.png
--------------------------------------------------------------------------------
/src/main/resources/panel/favicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/src/main/resources/panel/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Node interface",
3 | "name": "Ergo node interface",
4 | "icons": "",
5 | "start_url": ".",
6 | "display": "standalone",
7 | "theme_color": "#000000",
8 | "background_color": "#ffffff"
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/resources/panel/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 |
--------------------------------------------------------------------------------
/src/main/resources/panel/service-worker.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Welcome to your Workbox-powered service worker!
3 | *
4 | * You'll need to register this file in your web app and you should
5 | * disable HTTP caching for this file too.
6 | * See https://goo.gl/nhQhGp
7 | *
8 | * The rest of the code is auto-generated. Please don't update this file
9 | * directly; instead, make changes to your Workbox build configuration
10 | * and re-run your build process.
11 | * See https://goo.gl/2aRDsh
12 | */
13 |
14 | importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
15 |
16 | importScripts(
17 | "/precache-manifest.5ec1b24f55df1ee3e942e6e6777944c3.js"
18 | );
19 |
20 | self.addEventListener('message', (event) => {
21 | if (event.data && event.data.type === 'SKIP_WAITING') {
22 | self.skipWaiting();
23 | }
24 | });
25 |
26 | workbox.core.clientsClaim();
27 |
28 | /**
29 | * The workboxSW.precacheAndRoute() method efficiently caches and responds to
30 | * requests for URLs in the manifest.
31 | * See https://goo.gl/S9QRab
32 | */
33 | self.__precacheManifest = [].concat(self.__precacheManifest || []);
34 | workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
35 |
36 | workbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL("/index.html"), {
37 |
38 | blacklist: [/^\/_/,/\/[^/?]+\.[^/]+$/],
39 | });
40 |
--------------------------------------------------------------------------------
/src/main/resources/panel/static/media/MaterialIcons-Regular.012cf6a1.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ergoplatform/ergo/fb0d3098ffe95a40057b42f8e68b25ae4dfa7f03/src/main/resources/panel/static/media/MaterialIcons-Regular.012cf6a1.woff
--------------------------------------------------------------------------------
/src/main/resources/panel/static/media/MaterialIcons-Regular.570eb838.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ergoplatform/ergo/fb0d3098ffe95a40057b42f8e68b25ae4dfa7f03/src/main/resources/panel/static/media/MaterialIcons-Regular.570eb838.woff2
--------------------------------------------------------------------------------
/src/main/resources/panel/static/media/MaterialIcons-Regular.a37b0c01.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ergoplatform/ergo/fb0d3098ffe95a40057b42f8e68b25ae4dfa7f03/src/main/resources/panel/static/media/MaterialIcons-Regular.a37b0c01.ttf
--------------------------------------------------------------------------------
/src/main/resources/panel/static/media/MaterialIcons-Regular.e79bfd88.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ergoplatform/ergo/fb0d3098ffe95a40057b42f8e68b25ae4dfa7f03/src/main/resources/panel/static/media/MaterialIcons-Regular.e79bfd88.eot
--------------------------------------------------------------------------------
/src/main/resources/panel/static/media/Roboto-Bold.ee7b96fa.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ergoplatform/ergo/fb0d3098ffe95a40057b42f8e68b25ae4dfa7f03/src/main/resources/panel/static/media/Roboto-Bold.ee7b96fa.ttf
--------------------------------------------------------------------------------
/src/main/resources/panel/static/media/Roboto-Light.fc84e998.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ergoplatform/ergo/fb0d3098ffe95a40057b42f8e68b25ae4dfa7f03/src/main/resources/panel/static/media/Roboto-Light.fc84e998.ttf
--------------------------------------------------------------------------------
/src/main/resources/panel/static/media/Roboto-Medium.d0884059.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ergoplatform/ergo/fb0d3098ffe95a40057b42f8e68b25ae4dfa7f03/src/main/resources/panel/static/media/Roboto-Medium.d0884059.ttf
--------------------------------------------------------------------------------
/src/main/resources/panel/static/media/Roboto-Regular.3e1af3ef.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ergoplatform/ergo/fb0d3098ffe95a40057b42f8e68b25ae4dfa7f03/src/main/resources/panel/static/media/Roboto-Regular.3e1af3ef.ttf
--------------------------------------------------------------------------------
/src/main/resources/panel/static/media/Roboto-Thin.89e2666c.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ergoplatform/ergo/fb0d3098ffe95a40057b42f8e68b25ae4dfa7f03/src/main/resources/panel/static/media/Roboto-Thin.89e2666c.ttf
--------------------------------------------------------------------------------
/src/main/resources/panel/static/media/close.feae5a5c.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/panel/static/media/copy.icon.835ebda7.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/panel/static/media/redo-arrow-symbol.e801de31.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
44 |
--------------------------------------------------------------------------------
/src/main/resources/panel/static/media/remove.94c0849a.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/main/resources/swagger-ui/README.md:
--------------------------------------------------------------------------------
1 | ## Introduction
2 | Ergo uses a fork of swagger-ui with support for large integers. This repository can be found [here](https://github.com/SethDusek/swagger-ui).
3 |
4 | You can update this swagger-ui file by first pulling from upstream in the swagger-ui repository. Then in the root of ergo project run the following:
5 |
6 | ``` shell
7 | $ rsync -av --existing ../swagger-ui/dist/ src/main/resources/swagger-ui/
8 | ```
9 |
--------------------------------------------------------------------------------
/src/main/resources/swagger-ui/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ergoplatform/ergo/fb0d3098ffe95a40057b42f8e68b25ae4dfa7f03/src/main/resources/swagger-ui/favicon-16x16.png
--------------------------------------------------------------------------------
/src/main/resources/swagger-ui/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ergoplatform/ergo/fb0d3098ffe95a40057b42f8e68b25ae4dfa7f03/src/main/resources/swagger-ui/favicon-32x32.png
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/GlobalConstants.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform
2 |
3 | /**
4 | * A singleton which holds constants needed around the whole Ergo Platform.
5 | */
6 | object GlobalConstants {
7 |
8 | /**
9 | * Name of dispatcher for actors processing API requests
10 | * (to avoid clashing between blockchain processing and API actors)
11 | */
12 | val ApiDispatcher = "api-dispatcher"
13 |
14 | val IndexerDispatcher = "indexer-dispatcher"
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/http/NodePanelRoute.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.http
2 |
3 | import akka.http.scaladsl.server.{Directives, Route}
4 |
5 | final case class NodePanelRoute() extends Directives {
6 |
7 | val route: Route =
8 | pathPrefix("panel")(getFromResource("panel/index.html")) ~
9 | getFromResourceDirectory("panel")
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/http/SwaggerRoute.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.http
2 |
3 | import akka.http.scaladsl.model.{ContentTypes, HttpEntity}
4 | import akka.http.scaladsl.server.{Directives, Route}
5 | import org.ergoplatform.settings.RESTApiSettings
6 |
7 | final case class SwaggerRoute(settings: RESTApiSettings, swaggerConfig: String)
8 | extends Directives {
9 |
10 | val route: Route =
11 | swaggerConfR ~
12 | path("swagger")(getFromResource("swagger-ui/index.html")) ~
13 | getFromResourceDirectory("swagger-ui")
14 |
15 | private def swaggerConfR: Route = (get & path("api-docs" / "swagger.conf")) {
16 | complete(HttpEntity(ContentTypes.`application/json`, swaggerConfig))
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/http/api/InfoApiRoute.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.http.api
2 |
3 | import akka.actor.{ActorRef, ActorRefFactory}
4 | import akka.http.scaladsl.model.ContentTypes
5 | import akka.http.scaladsl.server.Route
6 | import akka.pattern.ask
7 | import io.circe.syntax._
8 | import io.circe.Json
9 | import org.ergoplatform.local.ErgoStatsCollector.{GetNodeInfo, NodeInfo}
10 | import org.ergoplatform.settings.RESTApiSettings
11 | import scorex.core.api.http.ApiResponse
12 |
13 |
14 | /**
15 | * API methods corresponding to /info path
16 | */
17 | case class InfoApiRoute(statsCollector: ActorRef,
18 | settings: RESTApiSettings)
19 | (implicit val context: ActorRefFactory) extends ErgoBaseApiRoute {
20 |
21 | override val route: Route = {
22 | (path("info") & get) {
23 | val timeJson = Map(
24 | "currentTime" -> System.currentTimeMillis().asJson
25 | ).asJson
26 | ApiResponse((statsCollector ? GetNodeInfo).mapTo[NodeInfo].map { nodeInfo =>
27 | nodeInfo.asJson.deepMerge(timeJson).deepMerge(Json.obj(
28 | "lastMemPoolUpdateTime" -> nodeInfo.lastMemPoolUpdateTime.asJson
29 | ))
30 | })
31 | } ~
32 | (path(".well-known" / "ai-plugin.json") & get) {
33 | getFromResource(".well-known/ai-plugin.json", ContentTypes.`application/json`)
34 | } ~
35 | (path("openapi.yaml") & get) {
36 | getFromResource("api/openapi-ai.yaml", ContentTypes.`text/plain(UTF-8)`)
37 | }
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/http/api/NodeApiRoute.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.http.api
2 |
3 | import akka.actor.{ActorRefFactory, ActorSystem}
4 | import akka.http.scaladsl.server.Route
5 | import org.ergoplatform.ErgoApp
6 | import org.ergoplatform.ErgoApp.RemoteShutdown
7 | import org.ergoplatform.settings.{ErgoSettings, RESTApiSettings}
8 | import scorex.core.api.http.ApiResponse
9 |
10 | import scala.concurrent.duration._
11 |
12 | case class NodeApiRoute(ergoSettings: ErgoSettings)(implicit system: ActorSystem, val context: ActorRefFactory) extends ErgoBaseApiRoute {
13 |
14 | val settings: RESTApiSettings = ergoSettings.scorexSettings.restApi
15 |
16 | override val route: Route = (pathPrefix("node") & withAuth) {
17 | shutdown
18 | }
19 |
20 | private val shutdownDelay = 5.seconds
21 |
22 | private def shutdown: Route = (pathPrefix("shutdown") & post) {
23 | system.scheduler.scheduleOnce(shutdownDelay)(ErgoApp.shutdownSystem(RemoteShutdown))
24 | ApiResponse(s"The node will be shut down in $shutdownDelay")
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/http/api/SortDirection.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.http.api
2 |
3 | /**
4 | * Encoded results sorting direction (in ascending or descending order)
5 | */
6 | object SortDirection {
7 |
8 | type Direction = Byte
9 |
10 | val ASC: Direction = 1.toByte
11 | val DESC: Direction = 0.toByte
12 | val INVALID: Direction = (-1).toByte
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/http/api/requests/CryptoResult.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.http.api.requests
2 |
3 | import sigma.data.SigmaBoolean
4 |
5 | /**
6 | * Result of reduction of ErgoTree for context provided (used in /script/executeWithContext)
7 | *
8 | * @param value - sigma-proposition (to be proven via a sigma-protocol)
9 | * @param cost - cost of the original script
10 | */
11 | case class CryptoResult(value: SigmaBoolean, cost: Long)
12 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/http/api/requests/ExecuteRequest.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.http.api.requests
2 |
3 | import org.ergoplatform.ErgoLikeContext
4 |
5 | /**
6 | * Represent a request for execution of a script in a given context.
7 | *
8 | * @param script ErgoScript source code of the contract to execute
9 | * @param env environment map of named constants used to compile the script
10 | * @param ctx script execution context
11 | */
12 | case class ExecuteRequest(script: String,
13 | env: Map[String,Any],
14 | ctx: ErgoLikeContext)
15 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/http/api/requests/HintExtractionRequest.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.http.api.requests
2 |
3 | import org.ergoplatform.modifiers.mempool.ErgoTransaction
4 | import sigma.data.SigmaBoolean
5 |
6 | /**
7 | * Represent a request for extracting prover hints from transaction.
8 | *
9 | * @param tx - transaction
10 | * @param real - real signers of transaction inputs
11 | * @param simulated - simulated signers of transaction inputs
12 | * @param inputs - hex-encoded input boxes bytes for the unsigned transaction (optional)
13 | * @param dataInputs - hex-encoded data-input boxes bytes for the unsigned transaction (optional)
14 | */
15 | case class HintExtractionRequest(tx: ErgoTransaction,
16 | real: Seq[SigmaBoolean],
17 | simulated: Seq[SigmaBoolean],
18 | inputs: Option[Seq[String]],
19 | dataInputs: Option[Seq[String]])
20 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/network/message/Message.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.network.message
2 |
3 | import akka.actor.DeadLetterSuppression
4 | import scorex.core.network.ConnectedPeer
5 |
6 | /**
7 | * Wrapper for a network message, whether come from external peer or generated locally
8 | *
9 | * @param spec - message specification
10 | * @param input - message being wrapped, whether in byte-array form (if from outside),
11 | * or structured data (if formed locally)
12 | * @param source - source peer, if the message is from outside
13 | * @tparam Content - message data type
14 | */
15 |
16 | case class Message[Content](
17 | spec: MessageSpec[Content],
18 | input: Either[Array[Byte], Content],
19 | source: Option[ConnectedPeer]
20 | ) extends MessageBase[Content]
21 | with DeadLetterSuppression
22 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/network/peer/PenaltyType.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.network.peer
2 |
3 | /**
4 | * A trait describing all possible types of the network participant misbehavior.
5 | * `penaltyScore` - a number defining how bad concrete kind of misbehavior is,
6 | * `isPermanent` - a flag defining whether a penalty is permanent.
7 | */
8 | sealed trait PenaltyType {
9 | val penaltyScore: Int
10 | val isPermanent: Boolean = false
11 | }
12 |
13 | object PenaltyType {
14 |
15 | case object NonDeliveryPenalty extends PenaltyType {
16 | override val penaltyScore: Int = 2
17 | }
18 |
19 | case object MisbehaviorPenalty extends PenaltyType {
20 | override val penaltyScore: Int = 10
21 | }
22 |
23 | case object SpamPenalty extends PenaltyType {
24 | override val penaltyScore: Int = 25
25 | }
26 |
27 | case object PermanentPenalty extends PenaltyType {
28 | override val penaltyScore: Int = 1000000000
29 | override val isPermanent: Boolean = true
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/nodeView/history/extra/ExtraIndex.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.history.extra
2 |
3 | import scorex.util.{ModifierId, bytesToId}
4 |
5 | /**
6 | * Base trait for all additional indexes made by ExtraIndexer
7 | */
8 | trait ExtraIndex {
9 | lazy val id: ModifierId = bytesToId(serializedId)
10 | def serializedId: Array[Byte]
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/BasicReaders.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.history.storage.modifierprocessors
2 |
3 | import org.ergoplatform.modifiers.{ErgoFullBlock, BlockSection}
4 | import scorex.util.ModifierId
5 |
6 | import scala.reflect.ClassTag
7 |
8 | /**
9 | * Interface for most basic and used functions reading objects
10 | * from history database
11 | */
12 | trait BasicReaders {
13 | def bestFullBlockOpt: Option[ErgoFullBlock]
14 |
15 | def headerIdsAtHeight(height: Int): Seq[ModifierId]
16 |
17 | /**
18 | * @return - best known header known for given height, if available
19 | */
20 | def bestHeaderIdAtHeight(height: Int): Option[ModifierId]
21 |
22 | def typedModifierById[T <: BlockSection : ClassTag](id: ModifierId): Option[T]
23 |
24 | def contains(id: ModifierId): Boolean
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/BlockSectionProcessor.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.history.storage.modifierprocessors
2 |
3 | import org.ergoplatform.consensus.ProgressInfo
4 | import org.ergoplatform.modifiers.{BlockSection, NonHeaderBlockSection}
5 | import org.ergoplatform.utils.ScorexEncoding
6 |
7 | import scala.util.Try
8 |
9 | /**
10 | * Trait that declares interfaces for validation and processing of various
11 | * block sections: BlockTransactions, ADProofs, etc.
12 | */
13 | trait BlockSectionProcessor extends ScorexEncoding {
14 |
15 | /**
16 | * Whether state requires to download adProofs before full block application
17 | */
18 | protected def requireProofs: Boolean
19 |
20 | /**
21 | * @param m - modifier to process
22 | * @return ProgressInfo - info required for State to be consistent with History
23 | */
24 | protected def process(m: NonHeaderBlockSection): Try[ProgressInfo[BlockSection]]
25 |
26 | /**
27 | * @param m - modifier to validate
28 | * @return Success() if modifier is valid from History point of view, Failure(error) otherwise
29 | */
30 | protected def validate(m: NonHeaderBlockSection): Try[Unit]
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/EmptyBlockSectionProcessor.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.history.storage.modifierprocessors
2 |
3 | import org.ergoplatform.consensus.ProgressInfo
4 | import org.ergoplatform.modifiers.{BlockSection, NonHeaderBlockSection}
5 |
6 | import scala.util.{Failure, Success, Try}
7 |
8 | /**
9 | * Trait that implements BlockSectionProcessor interfaces for a regime where the node is only
10 | * downloading block headers
11 | */
12 | trait EmptyBlockSectionProcessor extends BlockSectionProcessor {
13 |
14 | override protected def process(m: NonHeaderBlockSection): Try[ProgressInfo[BlockSection]] =
15 | Success(ProgressInfo[BlockSection](None, Seq.empty, Seq.empty, Seq.empty))
16 |
17 | override protected def validate(m: NonHeaderBlockSection): Try[Unit] =
18 | Failure(new Error("Regime that does not support block sections processing"))
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/MinimalFullBlockHeightFunctions.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.history.storage.modifierprocessors
2 |
3 | import org.ergoplatform.nodeView.history.ErgoHistoryUtils.Height
4 |
5 | /**
6 | * Interface to methods providing updating and reading height of first full block stored in local database
7 | */
8 | trait MinimalFullBlockHeightFunctions {
9 |
10 | /**
11 | * @return minimal height to applu full blocks from. Its value depends on node settings,
12 | * if bootstrapping with UTXO set snapshot is used, the value is being set to a first block after the snapshot,
13 | * for other modes, if blockToKeep > 0, the value is being set to a first block of blockchain suffix
14 | * after headers downloaded, and the value is updated when new blocks added to the chain. If blockToKeep == 0,
15 | * min full block height is set to genesis block height (1).
16 | */
17 | def readMinimalFullBlockHeight(): Height
18 |
19 | /**
20 | * Update min full block height, see `readMinimalFullBlockHeight` scaladoc on how it should be done
21 | */
22 | def writeMinimalFullBlockHeight(height: Height): Unit
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/nodeView/mempool/FeeHistogramBin.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.mempool
2 |
3 | import io.circe.{Encoder, Json}
4 | import io.circe.syntax._
5 |
6 | case class FeeHistogramBin(nTxns: Int, totalFee: Long)
7 |
8 | object FeeHistogramBin {
9 |
10 | implicit val encodeHistogramBin: Encoder[FeeHistogramBin] = (bin: FeeHistogramBin) => Json.obj(
11 | ("nTxns", bin.nTxns.asJson),
12 | ("totalFee", bin.totalFee.asJson)
13 | )
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/nodeView/mempool/HistogramStats.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.mempool
2 |
3 | import org.ergoplatform.nodeView.mempool.OrderedTxPool.WeightedTxId
4 |
5 | object HistogramStats {
6 |
7 | def getFeeHistogram(currTime: Long, nBins : Int, maxWaitTimeMsec: Long, wtxs : Seq[WeightedTxId]): Array[FeeHistogramBin] = {
8 | val histogram = Array.fill(nBins + 1)(FeeHistogramBin(0,0))
9 | val interval = maxWaitTimeMsec / nBins
10 | for (wtx <- wtxs) {
11 | val waitTime = currTime - wtx.created
12 | val bin = if (waitTime < maxWaitTimeMsec) (waitTime/interval).toInt else nBins
13 | histogram.update(bin, FeeHistogramBin(histogram(bin).nTxns + 1, histogram(bin).totalFee + wtx.feePerFactor))
14 | }
15 | histogram
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/nodeView/wallet/BalancesSnapshot.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.wallet
2 |
3 | import org.ergoplatform.nodeView.history.ErgoHistoryUtils.Height
4 | import org.ergoplatform.sdk.wallet.TokensMap
5 |
6 | final case class BalancesSnapshot(height: Height, balance: Long, assetBalances: TokensMap)
7 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoAddressJsonEncoder.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.wallet
2 |
3 | import io.circe.syntax._
4 | import io.circe.{Decoder, DecodingFailure, Encoder}
5 | import org.ergoplatform.settings.ChainSettings
6 | import org.ergoplatform.{ErgoAddress, ErgoAddressEncoder}
7 |
8 | import scala.util.{Failure, Success}
9 |
10 | case class ErgoAddressJsonEncoder(chainSettings: ChainSettings) {
11 |
12 | implicit val encoder: Encoder[ErgoAddress] = { address =>
13 | ErgoAddressEncoder(chainSettings.addressPrefix).toString(address).asJson
14 | }
15 |
16 | implicit val decoder: Decoder[ErgoAddress] = { cursor =>
17 | def decodeString(addrStr: String) = {
18 | ErgoAddressEncoder(chainSettings.addressPrefix).fromString(addrStr) match {
19 | case Success(address) => Right(address)
20 | case Failure(exception) => Left(DecodingFailure(exception.toString, cursor.history))
21 | }
22 | }
23 |
24 | for {
25 | addressStr <- cursor.as[String]
26 | address <- decodeString(addressStr)
27 | } yield address
28 | }
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceUtils.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.wallet
2 |
3 | import org.ergoplatform._
4 | import org.ergoplatform.sdk.wallet.secrets.{DerivationPath, ExtendedSecretKey}
5 | import org.ergoplatform.wallet.boxes.ErgoBoxSerializer
6 | import scorex.util.encode.Base16
7 | import scala.util.Try
8 |
9 | /**
10 | * Additional types and functions used in ErgoWalletService
11 | */
12 | object ErgoWalletServiceUtils {
13 | /**
14 | * Result of "deriveNextKey" operation
15 | */
16 | case class DeriveNextKeyResult(result: Try[(DerivationPath, P2PKAddress, ExtendedSecretKey)])
17 |
18 | // A helper which is deserializing Base16-encoded boxes to ErgoBox instances
19 | def stringsToBoxes(strings: Seq[String]): Seq[ErgoBox] =
20 | strings.map(in => Base16.decode(in).flatMap(ErgoBoxSerializer.parseBytesTry)).map(_.get)
21 |
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/nodeView/wallet/FilteringOptions.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.wallet
2 |
3 | /**
4 | * Request to filter transactions by height or by confirmations
5 | */
6 |
7 | sealed trait WalletFiltering
8 |
9 | case class FilterByHeight(minHeight: Int, maxHeight: Int) extends WalletFiltering
10 |
11 | case class FilterByConfirmations(minConfNum: Int, maxConfNum: Int) extends WalletFiltering
12 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/nodeView/wallet/IdUtils.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.wallet
2 |
3 | import org.ergoplatform.ErgoBox.{BoxId, TokenId}
4 | import org.ergoplatform.settings.Algos
5 | import scorex.crypto.authds.ADKey
6 | import scorex.util.ModifierId
7 | import sigma.data.Digest32Coll
8 | import sigma.Extensions.ArrayOps
9 | import supertagged.TaggedType
10 |
11 | object IdUtils {
12 |
13 | object EncodedBoxId extends TaggedType[String]
14 |
15 | type EncodedBoxId = EncodedBoxId.Type
16 |
17 | type EncodedTokenId = ModifierId
18 |
19 | def encodedBoxId(id: BoxId): EncodedBoxId = EncodedBoxId @@ Algos.encode(id)
20 |
21 | def decodedBoxId(id: EncodedBoxId): BoxId = ADKey @@ Algos.decode(id)
22 | .getOrElse(throw new Error("Failed to decode box id"))
23 |
24 | def encodedTokenId(id: TokenId): EncodedTokenId = ModifierId @@ Algos.encode(id)
25 |
26 | def decodedTokenId(id: EncodedTokenId): TokenId =
27 | Digest32Coll @@ (Algos.decode(id).getOrElse(throw new Error("Failed to decode token id"))).toColl
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/nodeView/wallet/WalletBox.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.wallet
2 |
3 | import io.circe.syntax._
4 | import io.circe.{Encoder, Json}
5 | import org.ergoplatform.ErgoAddressEncoder
6 | import org.ergoplatform.http.api.ApiCodecs
7 | import org.ergoplatform.http.api.ApiEncoderOption.{Detalization, ShowDetails}
8 | import org.ergoplatform.wallet.boxes.TrackedBox
9 |
10 | case class WalletBox(trackedBox: TrackedBox, confirmationsNumOpt: Option[Int])
11 |
12 | object WalletBox extends ApiCodecs {
13 |
14 | implicit val detalization: Detalization = ShowDetails
15 |
16 | implicit def encoder(implicit ae: ErgoAddressEncoder): Encoder[WalletBox] = { obj =>
17 | obj.trackedBox.asJson.deepMerge(
18 | Json.obj(
19 | "confirmationsNum" -> obj.confirmationsNumOpt.asJson,
20 | "address" -> ae.fromProposition(obj.trackedBox.box.ergoTree)
21 | .toOption
22 | .map(_.toString)
23 | .asJson
24 | )
25 | )
26 | }
27 |
28 | def apply(trackedBox: TrackedBox, currentHeight: Int): WalletBox = {
29 | WalletBox(trackedBox, trackedBox.inclusionHeightOpt.map(currentHeight - _))
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/nodeView/wallet/models/ChangeBox.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.wallet.models
2 |
3 | import io.circe.generic.encoding.DerivedAsObjectEncoder.deriveEncoder
4 | import io.circe.syntax._
5 | import io.circe.{Encoder, Json, KeyEncoder}
6 | import scorex.util.ModifierId
7 |
8 | /**
9 | * Box model for Wallet API
10 | *
11 | * @param value - Amount of Ergs
12 | * @param tokens - IDs and amounts of other tokens
13 | */
14 | final case class ChangeBox(value: Long, tokens: Map[ModifierId, Long])
15 |
16 | object ChangeBox {
17 |
18 | implicit val modifierIdEncoder: KeyEncoder[ModifierId] = KeyEncoder.instance(_.asInstanceOf[String])
19 |
20 | implicit val encoder: Encoder[ChangeBox] = box =>
21 | Json.obj(
22 | "value" -> box.value.asJson,
23 | "tokens" -> box.tokens.asJson
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/nodeView/wallet/models/CollectedBoxes.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.wallet.models
2 |
3 | import io.circe.generic.encoding.DerivedAsObjectEncoder.deriveEncoder
4 | import io.circe.syntax._
5 | import io.circe.{Encoder, Json}
6 | import org.ergoplatform.ErgoBox
7 | import org.ergoplatform.sdk.JsonCodecs
8 |
9 | /**
10 | * Response for requested boxes that contains ErgoBoxes and ChangeBoxes
11 | *
12 | * @param boxes - ErgoBoxes that satisfy user's request
13 | * @param changeBoxes - Boxes with excessive tokens and ergs
14 | */
15 | final case class CollectedBoxes(boxes: Seq[ErgoBox], changeBoxes: Seq[ChangeBox])
16 |
17 | object CollectedBoxes extends JsonCodecs {
18 |
19 | implicit val encoder: Encoder[CollectedBoxes] = request =>
20 | Json.obj(
21 | "boxes" -> request.boxes.asJson,
22 | "changeBoxes" -> request.changeBoxes.asJson
23 | )
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/Balance.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.wallet.persistence
2 |
3 | import org.ergoplatform.nodeView.wallet.IdUtils.{EncodedBoxId, encodedBoxId}
4 | import org.ergoplatform.wallet.boxes.TrackedBox
5 | import scorex.util.{ModifierId, bytesToId}
6 |
7 | case class Balance(id: EncodedBoxId,
8 | value: Long,
9 | assets: Map[ModifierId, Long])
10 |
11 | object Balance {
12 | def apply(tb: TrackedBox): Balance = Balance(encodedBoxId(tb.box.id), tb.box.value,
13 | tb.box.additionalTokens.toArray.map(x => (bytesToId(x._1.toArray), x._2)).toMap)
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/nodeView/wallet/requests/BoxesRequest.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.wallet.requests
2 |
3 | import io.circe.generic.decoding.DerivedDecoder.deriveDecoder
4 | import io.circe.{Decoder, KeyDecoder}
5 | import org.ergoplatform.ErgoBox
6 | import scorex.util.encode.Base16
7 | import sigmastate.eval.Extensions.ArrayByteOps
8 |
9 | /**
10 | * A request for boxes with given balance and assets
11 | */
12 | case class BoxesRequest(targetBalance: Long, targetAssets: Map[ErgoBox.TokenId, Long])
13 |
14 | object BoxesRequest {
15 |
16 | implicit val keyDecoder: KeyDecoder[ErgoBox.TokenId] =
17 | KeyDecoder.instance(s => Base16.decode(s).toOption.map(_.toTokenId))
18 |
19 | implicit val decoder: Decoder[BoxesRequest] =
20 | cursor =>
21 | for {
22 | targetBalance <- cursor.downField("targetBalance").as[Long]
23 | targetAssets <- cursor.downField("targetAssets").as[Map[ErgoBox.TokenId, Long]]
24 | } yield BoxesRequest(targetBalance, targetAssets)
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/nodeView/wallet/requests/BurnTokensRequest.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.wallet.requests
2 |
3 | import io.circe.syntax._
4 | import io.circe.{Decoder, Encoder, HCursor, Json}
5 | import org.ergoplatform.modifiers.mempool.ErgoTransaction._
6 | import org.ergoplatform.ErgoBox
7 |
8 | /**
9 | * Request for asset burning.
10 | *
11 | * @param assetsToBurn sequence of token id's and amount to burn
12 | *
13 | */
14 | case class BurnTokensRequest(assetsToBurn: Seq[(ErgoBox.TokenId, Long)])
15 | extends TransactionGenerationRequest
16 |
17 | class BurnTokensRequestEncoder extends Encoder[BurnTokensRequest] {
18 | def apply(request: BurnTokensRequest): Json = {
19 | Json.obj(
20 | "assetsToBurn" -> request.assetsToBurn.asJson
21 | )
22 | }
23 |
24 | }
25 |
26 | class BurnTokensRequestDecoder extends Decoder[BurnTokensRequest] {
27 | def apply(cursor: HCursor): Decoder.Result[BurnTokensRequest] = {
28 | for {
29 | assetsToBurn <- cursor.downField("assetsToBurn").as[Option[Seq[(ErgoBox.TokenId, Long)]]]
30 | } yield BurnTokensRequest(assetsToBurn.toSeq.flatten)
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/nodeView/wallet/requests/ExternalSecret.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.wallet.requests
2 |
3 | import org.ergoplatform.sdk.wallet.secrets.PrimitiveSecretKey
4 |
5 | /**
6 | * Externally provided secret (to be used once for a transaction to sign)
7 | *
8 | * @param key - the secret
9 | */
10 | case class ExternalSecret(key: PrimitiveSecretKey)
11 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/nodeView/wallet/requests/GenerateCommitmentsRequest.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.wallet.requests
2 |
3 | import org.ergoplatform.modifiers.mempool.UnsignedErgoTransaction
4 | import org.ergoplatform.sdk.wallet.secrets.{DhtSecretKey, DlogSecretKey}
5 |
6 | /**
7 | * A request to generate commitments for unsigned transaction, useful for multi-party signing.
8 | *
9 | * @param unsignedTx - unsigned transaction
10 | * @param externalSecretsOpt - optionally, externally provided secrets
11 | * @param inputs - hex-encoded input boxes bytes for the unsigned transaction (optional)
12 | * @param dataInputs - hex-encoded data-input boxes bytes for the unsigned transaction (optional)
13 | */
14 | case class GenerateCommitmentsRequest(unsignedTx: UnsignedErgoTransaction,
15 | externalSecretsOpt: Option[Seq[ExternalSecret]],
16 | inputs: Option[Seq[String]],
17 | dataInputs: Option[Seq[String]]) {
18 |
19 | lazy val externalSecrets: Seq[ExternalSecret] = externalSecretsOpt.getOrElse(Seq.empty)
20 |
21 | lazy val dlogs: Seq[DlogSecretKey] = externalSecrets.collect { case ExternalSecret(d: DlogSecretKey) => d }
22 |
23 | lazy val dhts: Seq[DhtSecretKey] = externalSecrets.collect { case ExternalSecret(d: DhtSecretKey) => d }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/nodeView/wallet/requests/TransactionSigningRequest.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.wallet.requests
2 |
3 | import org.ergoplatform.modifiers.mempool.UnsignedErgoTransaction
4 | import org.ergoplatform.sdk.wallet.secrets.{DhtSecretKey, DlogSecretKey}
5 | import org.ergoplatform.wallet.interpreter.TransactionHintsBag
6 |
7 | /**
8 | * A request to sign a transaction
9 | *
10 | * @param unsignedTx - unsigned transaction
11 | * @param hints - hints for interpreter (such as additional one-time secrets)
12 | * @param externalSecrets - externally provided secrets
13 | * @param inputs - hex-encoded input boxes bytes for the unsigned transaction (optional)
14 | * @param dataInputs - hex-encoded data-input boxes bytes for the unsigned transaction (optional)
15 | */
16 | case class TransactionSigningRequest(unsignedTx: UnsignedErgoTransaction,
17 | hints: TransactionHintsBag,
18 | externalSecrets: Seq[ExternalSecret],
19 | inputs: Option[Seq[String]],
20 | dataInputs: Option[Seq[String]]) {
21 |
22 | lazy val dlogs: Seq[DlogSecretKey] = externalSecrets.collect { case ExternalSecret(d: DlogSecretKey) => d }
23 |
24 | lazy val dhts: Seq[DhtSecretKey] = externalSecrets.collect { case ExternalSecret(d: DhtSecretKey) => d }
25 |
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/settings/Args.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.settings
2 |
3 | final case class Args(userConfigPathOpt: Option[String] = None, networkTypeOpt: Option[NetworkType] = None)
4 |
5 | object Args {
6 | def empty: Args = Args(None, None)
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/settings/CacheSettings.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.settings
2 |
3 | import scala.concurrent.duration.FiniteDuration
4 |
5 | /**
6 | * Configuration file for different caches
7 | *
8 | * @see src/main/resources/application.conf for parameters description
9 | */
10 | case class CacheSettings(
11 | history: HistoryCacheSettings,
12 | network: NetworkCacheSettings,
13 | mempool: MempoolCacheSettings
14 | )
15 |
16 | case class HistoryCacheSettings(blockSectionsCacheSize: Int, extraCacheSize: Int, headersCacheSize: Int, indexesCacheSize: Int)
17 |
18 | case class NetworkCacheSettings(
19 | invalidModifiersCacheSize: Int,
20 | invalidModifiersCacheExpiration: FiniteDuration
21 | )
22 |
23 | case class MempoolCacheSettings(
24 | invalidModifiersCacheSize: Int,
25 | invalidModifiersCacheExpiration: FiniteDuration
26 | )
27 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/settings/NetworkType.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.settings
2 |
3 | sealed trait NetworkType {
4 | val verboseName: String
5 | def isMainNet: Boolean = false
6 | }
7 |
8 | object NetworkType {
9 |
10 | def all: Seq[NetworkType] = Seq(MainNet, TestNet, DevNet)
11 |
12 | def fromString(name: String): Option[NetworkType] = all.find(_.verboseName == name)
13 |
14 | case object MainNet extends NetworkType {
15 | val verboseName: String = "mainnet"
16 | override def isMainNet: Boolean = true
17 | }
18 |
19 | case object TestNet extends NetworkType {
20 | val verboseName: String = "testnet"
21 | }
22 |
23 | case object DevNet extends NetworkType {
24 | val verboseName: String = "devnet"
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/settings/StateTypeReaders.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.settings
2 |
3 | import com.typesafe.config.ConfigException
4 | import org.ergoplatform.nodeView.state._
5 |
6 | trait StateTypeReaders {
7 |
8 | def stateTypeFromString(typeName: String, path: String): StateType = {
9 | StateType.values.find(_.stateTypeName == typeName)
10 | .getOrElse(throw new ConfigException.BadValue(path, typeName))
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/settings/VotingTargets.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.settings
2 |
3 | import com.typesafe.config.Config
4 | import net.ceedubs.ficus.Ficus._
5 | import org.ergoplatform.settings.ErgoSettings.configPath
6 |
7 | import scala.collection.JavaConverters._
8 | import scala.util.Try
9 |
10 | /**
11 | * Local miner settings to determine how to vote
12 | *
13 | * @param targets - desired parameters targets
14 | * @param desiredUpdate - rules to deactivate if soft-fork is desirable
15 | */
16 | case class VotingTargets(targets: Map[Byte, Int], desiredUpdate: ErgoValidationSettingsUpdate) {
17 | val softForkOption: Option[Int] = targets.get(Parameters.SoftFork)
18 | val softFork: Int = softForkOption.getOrElse(0)
19 | }
20 |
21 | object VotingTargets {
22 |
23 | val empty: VotingTargets = VotingTargets(Map(), ErgoValidationSettingsUpdate.empty)
24 |
25 | def fromConfig(config: Config): VotingTargets = {
26 | val votingObject = config.getObject(s"$configPath.voting")
27 | val toDisable = config.getConfig(s"$configPath.voting").as[Array[Int]](s"rulesToDisable")
28 |
29 | val parameterTargets = votingObject
30 | .keySet()
31 | .asScala
32 | .flatMap(id => Try(id.toByte -> votingObject.get(id).render().toInt).toOption)
33 | .toMap
34 | val desiredUpdate = ErgoValidationSettingsUpdate(toDisable.map(_.toShort), Seq())
35 |
36 | VotingTargets(parameterTargets, desiredUpdate)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/scala/org/ergoplatform/settings/WalletSettings.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.settings
2 |
3 | import org.ergoplatform.nodeView.wallet.WalletProfile
4 | import org.ergoplatform.wallet.settings.SecretStorageSettings
5 |
6 | case class WalletSettings(secretStorage: SecretStorageSettings,
7 | seedStrengthBits: Int,
8 | mnemonicPhraseLanguage: String,
9 | usePreEip3Derivation: Boolean = false,
10 | keepSpentBoxes: Boolean = false,
11 | defaultTransactionFee: Long = 1000000L,
12 | dustLimit: Option[Long] = None,
13 | maxInputs: Int = 100,
14 | optimalInputs: Int = 3,
15 | testMnemonic: Option[String] = None,
16 | testKeysQty: Option[Int] = None,
17 | // Some(Seq(x)) burns all except x, Some(Seq.empty) burns all, None ignores that feature
18 | tokensWhitelist: Option[Seq[String]] = None,
19 | checkEIP27: Boolean = false,
20 | profile: String = WalletProfile.User.label) {
21 |
22 | val walletProfile: WalletProfile = WalletProfile.fromLabel(profile)
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/scala/scorex/core/api/http/ApiDirectives.scala:
--------------------------------------------------------------------------------
1 | package scorex.core.api.http
2 |
3 | import akka.http.scaladsl.server.{AuthorizationFailedRejection, Directive0}
4 | import org.ergoplatform.settings.RESTApiSettings
5 | import org.ergoplatform.utils.ScorexEncoding
6 | import scorex.crypto.hash.Blake2b256
7 |
8 | trait ApiDirectives extends CorsHandler with ScorexEncoding {
9 | val settings: RESTApiSettings
10 | val apiKeyHeaderName: String
11 |
12 | lazy val withAuth: Directive0 = optionalHeaderValueByName(apiKeyHeaderName).flatMap {
13 | case _ if settings.apiKeyHash.isEmpty => pass
14 | case None => reject(AuthorizationFailedRejection)
15 | case Some(key) =>
16 | val keyHashStr: String = encoder.encode(Blake2b256(key))
17 | if (settings.apiKeyHash.contains(keyHashStr)) {
18 | pass
19 | } else {
20 | reject(AuthorizationFailedRejection)
21 | }
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/scala/scorex/core/api/http/ApiErrorHandler.scala:
--------------------------------------------------------------------------------
1 | package scorex.core.api.http
2 |
3 | import akka.http.scaladsl.server.ExceptionHandler
4 | import org.ergoplatform.http.api.ApiError
5 |
6 | import scala.util.control.NonFatal
7 |
8 | object ApiErrorHandler {
9 |
10 | implicit val exceptionHandler: ExceptionHandler = ExceptionHandler {
11 | case NonFatal(e) => ApiError(e)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/scala/scorex/core/api/http/ApiRoute.scala:
--------------------------------------------------------------------------------
1 | package scorex.core.api.http
2 |
3 | import akka.actor.ActorRefFactory
4 | import akka.http.scaladsl.server.Route
5 | import akka.http.scaladsl.unmarshalling.PredefinedFromEntityUnmarshallers
6 | import akka.util.Timeout
7 | import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport
8 | import io.circe.Printer
9 | import scorex.core.utils.ActorHelper
10 | import scorex.util.ScorexLogging
11 |
12 | trait ApiRoute
13 | extends ApiDirectives
14 | with ActorHelper
15 | with FailFastCirceSupport
16 | with PredefinedFromEntityUnmarshallers
17 | with ScorexLogging {
18 |
19 | def context: ActorRefFactory
20 | def route: Route
21 |
22 | //TODO: should we move it to the settings?
23 | override val apiKeyHeaderName: String = "api_key"
24 |
25 | implicit val printer: Printer = Printer.spaces2.copy(dropNullValues = true)
26 | implicit lazy val timeout: Timeout = Timeout(settings.timeout)
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/scala/scorex/core/api/http/CompositeHttpService.scala:
--------------------------------------------------------------------------------
1 | package scorex.core.api.http
2 |
3 | import akka.actor.ActorSystem
4 | import akka.http.scaladsl.model.StatusCodes
5 | import akka.http.scaladsl.server.Route
6 | import akka.http.scaladsl.server.directives.RouteDirectives
7 | import org.ergoplatform.settings.RESTApiSettings
8 | import scorex.core.api.http.swagger.SwaggerConfigRoute
9 |
10 | case class CompositeHttpService(system: ActorSystem, routes: Seq[ApiRoute], settings: RESTApiSettings, swaggerConf: String)
11 | extends CorsHandler {
12 |
13 | implicit val actorSystem: ActorSystem = system
14 |
15 | val swaggerService: SwaggerConfigRoute = new SwaggerConfigRoute(swaggerConf: String, settings: RESTApiSettings)
16 |
17 | val redirectToSwagger: Route = path("" | "/") {
18 | redirect("/swagger", StatusCodes.PermanentRedirect)
19 | }
20 |
21 | val compositeRoute: Route =
22 | routes.map(_.route).reduceOption(_ ~ _).getOrElse(RouteDirectives.reject) ~
23 | swaggerService.route ~
24 | path("swagger")(getFromResource("swagger-ui/index.html")) ~
25 | getFromResourceDirectory("swagger-ui") ~
26 | redirectToSwagger
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/scala/scorex/core/api/http/CorsHandler.scala:
--------------------------------------------------------------------------------
1 | package scorex.core.api.http
2 |
3 | import akka.http.scaladsl.marshalling.ToResponseMarshallable.apply
4 | import akka.http.scaladsl.model.HttpMethods._
5 | import akka.http.scaladsl.model.headers._
6 | import akka.http.scaladsl.model.{HttpResponse, StatusCodes}
7 | import akka.http.scaladsl.server.Directive.addByNameNullaryApply
8 | import akka.http.scaladsl.server.{Directive0, Directives, Route}
9 |
10 | /**
11 | * Provides tools for handling a Cross-Origin Resource Sharing spec workflow
12 | * (including `OPTIONS` pre-flight requests).
13 | */
14 | trait CorsHandler extends Directives {
15 |
16 | private val corsResponseHeaders: List[ModeledHeader] = List[ModeledHeader](
17 | `Access-Control-Allow-Origin`.*,
18 | `Access-Control-Allow-Credentials`(true),
19 | `Access-Control-Allow-Headers`("Authorization", "Content-Type", "X-Requested-With", "api_key")
20 | )
21 |
22 | def corsHandler(r: Route): Route = addAccessControlHeaders {
23 | preflightRequestHandler ~ r
24 | }
25 |
26 | def addCorsHeaders(response: HttpResponse): HttpResponse =
27 | response.withHeaders(corsResponseHeaders)
28 |
29 | private def addAccessControlHeaders: Directive0 =
30 | respondWithHeaders(corsResponseHeaders)
31 |
32 | private def preflightRequestHandler: Route = options {
33 | complete {
34 | HttpResponse(StatusCodes.OK)
35 | .withHeaders(`Access-Control-Allow-Methods`(OPTIONS, POST, PUT, GET, DELETE))
36 | }
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/scala/scorex/core/api/http/swagger/SwaggerConfigRoute.scala:
--------------------------------------------------------------------------------
1 | package scorex.core.api.http.swagger
2 |
3 | import akka.actor.ActorRefFactory
4 | import akka.http.scaladsl.model.{ContentTypes, HttpEntity}
5 | import akka.http.scaladsl.server.Route
6 | import org.ergoplatform.settings.RESTApiSettings
7 | import scorex.core.api.http.ApiRoute
8 |
9 | class SwaggerConfigRoute(swaggerConf: String, override val settings: RESTApiSettings)(implicit val context: ActorRefFactory)
10 | extends ApiRoute {
11 |
12 | override val route: Route = {
13 | (get & path("api-docs" / "swagger.conf")) {
14 | complete(HttpEntity(ContentTypes.`application/json`, swaggerConf))
15 | }
16 | }
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/src/main/scala/scorex/core/app/ScorexContext.scala:
--------------------------------------------------------------------------------
1 | package scorex.core.app
2 |
3 | import java.net.InetSocketAddress
4 |
5 | import scorex.core.network.UPnPGateway
6 | import org.ergoplatform.network.message.MessageSpec
7 |
8 | case class ScorexContext(messageSpecs: Seq[MessageSpec[_]],
9 | upnpGateway: Option[UPnPGateway],
10 | externalNodeAddress: Option[InetSocketAddress])
11 |
--------------------------------------------------------------------------------
/src/main/scala/scorex/core/network/ConnectionDescription.scala:
--------------------------------------------------------------------------------
1 | package scorex.core.network
2 |
3 | import java.net.InetSocketAddress
4 | import akka.actor.ActorRef
5 | import org.ergoplatform.network.PeerFeature
6 |
7 | case class ConnectionDescription(connection: ActorRef,
8 | connectionId: ConnectionId,
9 | ownSocketAddress: Option[InetSocketAddress],
10 | localFeatures: Seq[PeerFeature])
11 |
--------------------------------------------------------------------------------
/src/main/scala/scorex/core/network/ConnectionDirection.scala:
--------------------------------------------------------------------------------
1 | package scorex.core.network
2 |
3 | sealed trait ConnectionDirection {
4 | def isIncoming: Boolean
5 | def isOutgoing: Boolean = !isIncoming
6 | }
7 |
8 | case object Incoming extends ConnectionDirection {
9 | override val isIncoming: Boolean = true
10 | }
11 |
12 | case object Outgoing extends ConnectionDirection {
13 | override val isIncoming: Boolean = false
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/scala/scorex/core/network/ConnectionId.scala:
--------------------------------------------------------------------------------
1 | package scorex.core.network
2 |
3 | import java.net.InetSocketAddress
4 |
5 | /**
6 | * Wraps (remoteAddress, localAddress, direction) tuple, which allows to precisely identify peer.
7 | */
8 | final case class ConnectionId(remoteAddress: InetSocketAddress,
9 | localAddress: InetSocketAddress,
10 | direction: ConnectionDirection) {
11 |
12 | override def toString: String =
13 | s"ConnectionId(remote=${remoteAddress.toString}, local=${localAddress.toString}, direction=$direction)"
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/scala/scorex/core/network/MaliciousBehaviorException.scala:
--------------------------------------------------------------------------------
1 | package scorex.core.network
2 |
3 | /**
4 | * Custom exception to distinguish malicious behaviour of external peers from non-adversarial network issues
5 | *
6 | * @param message - exception message
7 | */
8 | case class MaliciousBehaviorException(message: String) extends Exception(message)
--------------------------------------------------------------------------------
/src/main/scala/scorex/core/network/ModifiersStatus.scala:
--------------------------------------------------------------------------------
1 | package scorex.core.network
2 |
3 | sealed trait ModifiersStatus
4 |
5 | object ModifiersStatus {
6 |
7 | /**
8 | * This modifier is unknown to our node
9 | */
10 | case object Unknown extends ModifiersStatus
11 |
12 | /**
13 | * Our node have requested this modifier from other peers but did not received it yet.
14 | */
15 | case object Requested extends ModifiersStatus
16 |
17 | /**
18 | * Our node have received this modifier from other peers but did not applied yet.
19 | * The modifier might be in ModifiersCache or on the way to it
20 | */
21 | case object Received extends ModifiersStatus
22 |
23 | /**
24 | * This modifier is already on NodeViewHoder - applied to History if it is block section or
25 | * in MemPool if it is transaction.
26 | */
27 | case object Held extends ModifiersStatus
28 |
29 | /**
30 | * This modifier is permanently invalid - never try to download it
31 | */
32 | case object Invalid extends ModifiersStatus
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/scala/scorex/core/network/SendingStrategy.scala:
--------------------------------------------------------------------------------
1 | package scorex.core.network
2 |
3 | import scala.util.Random
4 |
5 | trait SendingStrategy {
6 | def choose(peers: Seq[ConnectedPeer]): Seq[ConnectedPeer]
7 | }
8 |
9 | object SendToRandom extends SendingStrategy {
10 | override def choose(peers: Seq[ConnectedPeer]): Seq[ConnectedPeer] = {
11 | if (peers.nonEmpty) {
12 | Seq(peers(Random.nextInt(peers.length)))
13 | } else {
14 | Nil
15 | }
16 | }
17 | }
18 |
19 | case object Broadcast extends SendingStrategy {
20 | override def choose(peers: Seq[ConnectedPeer]): Seq[ConnectedPeer] = peers
21 | }
22 |
23 | case class SendToPeer(chosenPeer: ConnectedPeer) extends SendingStrategy {
24 | override def choose(peers: Seq[ConnectedPeer]): Seq[ConnectedPeer] = Seq(chosenPeer)
25 | }
26 |
27 | case class SendToPeers(chosenPeers: Seq[ConnectedPeer]) extends SendingStrategy {
28 | override def choose(peers: Seq[ConnectedPeer]): Seq[ConnectedPeer] = chosenPeers
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/scala/scorex/core/utils/ActorHelper.scala:
--------------------------------------------------------------------------------
1 | package scorex.core.utils
2 |
3 | import akka.actor.ActorRef
4 | import akka.pattern.ask
5 | import akka.util.Timeout
6 |
7 | import scala.concurrent.Future
8 | import scala.reflect.ClassTag
9 |
10 | /**
11 | * Helper that encapsulates ask patter for actors and returns Future[_]
12 | */
13 | trait ActorHelper {
14 |
15 | def askActor[A: ClassTag](actorRef: ActorRef, question: Any)
16 | (implicit timeout: Timeout): Future[A] = (actorRef ? question).mapTo[A]
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/scala/scorex/core/utils/NetworkUtils.scala:
--------------------------------------------------------------------------------
1 | package scorex.core.utils
2 |
3 | import java.net.{Inet4Address, InetSocketAddress, NetworkInterface}
4 | import scala.collection.JavaConverters._
5 |
6 | object NetworkUtils {
7 |
8 | def getListenAddresses(bindAddress: InetSocketAddress): Set[InetSocketAddress] = {
9 | if (bindAddress.getAddress.isAnyLocalAddress || bindAddress.getAddress.isLoopbackAddress) {
10 | NetworkInterface.getNetworkInterfaces.asScala
11 | .flatMap(_.getInetAddresses.asScala)
12 | .collect { case a: Inet4Address => a}
13 | .map(a => new InetSocketAddress(a, bindAddress.getPort))
14 | .toSet
15 | } else {
16 | Set(bindAddress)
17 | }
18 | }
19 |
20 | def isSelf(peerAddress: InetSocketAddress,
21 | bindAddress: InetSocketAddress,
22 | externalNodeAddress: Option[InetSocketAddress]): Boolean = {
23 | NetworkUtils.getListenAddresses(bindAddress).contains(peerAddress) ||
24 | externalNodeAddress.contains(peerAddress)
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/scala/scorex/util/serialization/VLQByteStringWriter.scala:
--------------------------------------------------------------------------------
1 | package scorex.util.serialization
2 |
3 | import akka.util.ByteString
4 |
5 | class VLQByteStringWriter extends VLQWriter {
6 | override type CH = ByteString
7 |
8 | @inline
9 | override def newWriter(): Writer.Aux[CH] = {
10 | new VLQByteStringWriter()
11 | }
12 |
13 | private val builder = ByteString.createBuilder
14 |
15 | @inline
16 | override def length(): Int = builder.length
17 |
18 | @inline
19 | override def putChunk(byteString: ByteString): this.type = {
20 | builder.append(byteString)
21 | this
22 | }
23 |
24 | @inline
25 | override def put(x: Byte): this.type = {
26 | builder.putByte(x)
27 | this
28 | }
29 |
30 | @inline
31 | override def putBoolean(x: Boolean): this.type = {
32 | val byte: Byte = if (x) 0x01 else 0x00
33 | builder.putByte(byte)
34 | this
35 | }
36 |
37 | override def putBytes(xs: Array[Byte],
38 | offset: Int,
39 | length: Int): VLQByteStringWriter.this.type = {
40 | builder.putBytes(xs, offset, length)
41 | this
42 | }
43 |
44 | @inline
45 | override def putBytes(xs: Array[Byte]): this.type = {
46 | builder.putBytes(xs)
47 | this
48 | }
49 |
50 | @inline
51 | override def result(): ByteString = {
52 | builder.result()
53 | }
54 |
55 | @inline override def toBytes: Array[Byte] = {
56 | builder.result().toArray
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | System.out
6 |
7 | WARN
8 |
9 |
10 | [%thread] >> [%-5level] %logger{36} >> %d{HH:mm:ss.SSS} %msg%n
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/test/resources/settings.conf:
--------------------------------------------------------------------------------
1 | {
2 | "ergo": {
3 | "node": {
4 | "blocksToKeep": 13,
5 | "mempoolSorting": "bySize"
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/src/test/resources/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "ergo": {
3 | "node": {
4 | "blocksToKeep": 12,
5 | "mempoolSorting": "bySize"
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/src/test/scala/org/ergoplatform/db/KvStoreReaderSpec.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.db
2 |
3 | import org.scalatest.matchers.should.Matchers
4 | import org.scalatest.propspec.AnyPropSpec
5 |
6 | class KvStoreReaderSpec extends AnyPropSpec with Matchers with DBSpec {
7 |
8 | property("getRange works properly") {
9 | withStore {store =>
10 | val keyStart = byteString("A")
11 | val keyEnd = byteString("Z")
12 | store.getRange(keyStart, keyEnd).length shouldBe 0
13 |
14 | store.insert(keyStart, keyStart).get
15 | store.getRange(keyStart, keyEnd).length shouldBe 1
16 |
17 | store.insert(keyEnd, keyEnd).get
18 | store.getRange(keyStart, keyEnd).length shouldBe 2
19 |
20 | // keys before the range
21 | store.getRange(byteString("a"), byteString("z")).length shouldBe 0
22 |
23 | // keys inside the range
24 | store.getRange(byteString("<"), byteString("z")).length shouldBe 2
25 |
26 | // keys after the range
27 | store.getRange(byteString("<"), byteString("?")).length shouldBe 0
28 |
29 | //removing keys
30 | store.remove(Array(keyStart, keyEnd)).get
31 | store.getRange(keyStart, keyEnd).length shouldBe 0
32 | }
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/test/scala/org/ergoplatform/nodeView/state/SnapshotsInfoSpecification.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.state
2 |
3 | import org.ergoplatform.utils.ErgoCorePropertyTest
4 |
5 | class SnapshotsInfoSpecification extends ErgoCorePropertyTest {
6 | import org.ergoplatform.utils.generators.ErgoCoreGenerators._
7 |
8 | property("makeEmpty / nonEmpty / withNewManifest") {
9 | val empty = SnapshotsInfo.empty
10 | empty.nonEmpty shouldBe false
11 |
12 | val h = 10
13 | val d = digest32Gen.sample.get
14 | val nonEmpty = empty.withNewManifest(h, d)
15 | nonEmpty.nonEmpty shouldBe true
16 | nonEmpty.availableManifests(h).sameElements(d) shouldBe true
17 |
18 | val h2 = 20
19 | val d2 = digest32Gen.sample.get
20 | val ne2 = nonEmpty.withNewManifest(h2, d2)
21 | nonEmpty.availableManifests.size shouldBe 1
22 | ne2.availableManifests(h).sameElements(d) shouldBe true
23 | ne2.availableManifests(h2).sameElements(d2) shouldBe true
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/test/scala/org/ergoplatform/nodeView/state/wrapped/WrappedDigestState.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.state.wrapped
2 |
3 | import org.ergoplatform.ErgoLikeContext.Height
4 | import org.ergoplatform.modifiers.BlockSection
5 | import org.ergoplatform.nodeView.state.DigestState
6 | import org.ergoplatform.settings.ErgoSettings
7 | import org.ergoplatform.core.VersionTag
8 | import org.ergoplatform.nodeView.LocallyGeneratedModifier
9 |
10 | import scala.util.Try
11 |
12 | class WrappedDigestState(val digestState: DigestState,
13 | val wrappedUtxoState: WrappedUtxoState,
14 | val settings: ErgoSettings)
15 | extends DigestState(digestState.version, digestState.rootDigest, digestState.store, settings) {
16 |
17 | override def applyModifier(mod: BlockSection, estimatedTip: Option[Height])
18 | (generate: LocallyGeneratedModifier => Unit): Try[WrappedDigestState] = {
19 | wrapped(super.applyModifier(mod, estimatedTip)(_ => ()), wrappedUtxoState.applyModifier(mod, estimatedTip)(_ => ()))
20 | }
21 |
22 | override def rollbackTo(version: VersionTag): Try[WrappedDigestState] = {
23 | wrapped(super.rollbackTo(version), wrappedUtxoState.rollbackTo(version))
24 | }
25 |
26 | private def wrapped(digestT: Try[DigestState], utxoT: Try[WrappedUtxoState]): Try[WrappedDigestState] =
27 | digestT.flatMap(digest => utxoT.map(utxo => new WrappedDigestState(digest, utxo, settings)))
28 | }
29 |
--------------------------------------------------------------------------------
/src/test/scala/org/ergoplatform/nodeView/wallet/WalletProfileSpec.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.wallet
2 |
3 | import org.ergoplatform.utils.ErgoCorePropertyTest
4 |
5 | import scala.util.Try
6 |
7 | class WalletProfileSpec extends ErgoCorePropertyTest {
8 | property("fromLabel getting profiles properly") {
9 | WalletProfile.fromLabel("user").outputsFilterSize shouldBe WalletProfile.User.outputsFilterSize
10 | WalletProfile.fromLabel("user").scriptsFilterSize shouldBe WalletProfile.User.scriptsFilterSize
11 |
12 | WalletProfile.fromLabel("exchange").scriptsFilterSize should not be WalletProfile.User.scriptsFilterSize
13 | WalletProfile.fromLabel("exchange").outputsFilterSize shouldBe WalletProfile.Exchange.outputsFilterSize
14 | WalletProfile.fromLabel("exchange").scriptsFilterSize shouldBe WalletProfile.Exchange.scriptsFilterSize
15 |
16 | Try(WalletProfile.fromLabel("appserver")).isFailure shouldBe true
17 | WalletProfile.fromLabel("appServer").outputsFilterSize shouldBe WalletProfile.AppServer.outputsFilterSize
18 | WalletProfile.fromLabel("appServer").scriptsFilterSize shouldBe WalletProfile.AppServer.scriptsFilterSize
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/test/scala/org/ergoplatform/nodeView/wallet/WalletVarsSpec.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.wallet
2 |
3 | import org.ergoplatform.utils.ErgoCorePropertyTest
4 | import org.ergoplatform.wallet.interpreter.ErgoProvingInterpreter
5 |
6 | class WalletVarsSpec extends ErgoCorePropertyTest {
7 | import org.ergoplatform.utils.ErgoCoreTestConstants._
8 | import org.ergoplatform.utils.ErgoNodeTestConstants._
9 |
10 | property(".withProver init") {
11 | val prover = ErgoProvingInterpreter(defaultRootSecret, parameters)
12 | val walletVars = WalletVars(None, Seq.empty, None)
13 | val wp = walletVars.withProver(prover)
14 |
15 | wp.trackedPubKeys.length shouldBe 1
16 | wp.trackedBytes.length shouldBe 1
17 |
18 | defaultRootSecret.publicKey shouldBe wp.trackedPubKeys.head
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/test/scala/org/ergoplatform/nodeView/wallet/scanning/ScanSpecification.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.wallet.scanning
2 |
3 | import io.circe.Json
4 | import org.ergoplatform.utils.ErgoCorePropertyTest
5 |
6 | class ScanSpecification extends ErgoCorePropertyTest {
7 | import org.ergoplatform.utils.generators.ErgoNodeWalletGenerators._
8 | import ScanJsonCodecs._
9 |
10 | property("external scan req json serialization roundtrip") {
11 | forAll(externalScanReqGen) { req =>
12 | val j: Json = scanReqEncoder.apply(req)
13 | scanReqDecoder.decodeJson(j).toTry.get == req
14 | }
15 | }
16 |
17 | property("external scan json serialization roundtrip") {
18 | forAll(externalAppGen) { req =>
19 | val j: Json = scanEncoder.apply(req)
20 | scanDecoder.decodeJson(j).toTry.get == req
21 | }
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/test/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateSerializerSpecification.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.nodeView.wallet.scanning
2 |
3 | import org.ergoplatform.utils.ErgoCorePropertyTest
4 |
5 | class ScanningPredicateSerializerSpecification extends ErgoCorePropertyTest {
6 | import org.ergoplatform.utils.generators.ErgoNodeWalletGenerators._
7 |
8 | property("complex or roundtrip") {
9 | forAll(scanningPredicateGen) { p =>
10 | val bs = ScanningPredicateSerializer.toBytes(p)
11 | ScanningPredicateSerializer.parseBytes(bs) == p
12 | }
13 | }
14 |
15 | property("complex and roundtrip") {
16 | forAll(scanningPredicateGen) { p =>
17 | val bs = ScanningPredicateSerializer.toBytes(p)
18 | ScanningPredicateSerializer.parseBytes(bs) == p
19 | }
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/test/scala/org/ergoplatform/serialization/SerializationTests.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.serialization
2 |
3 | import org.ergoplatform.modifiers.history.popow.NipopowProofSerializer
4 | import org.ergoplatform.network.ErgoNodeViewSynchronizer
5 | import org.ergoplatform.nodeView.wallet.persistence.WalletDigestSerializer
6 | import org.ergoplatform.utils.ErgoCorePropertyTest
7 | import org.ergoplatform.utils.ErgoCoreTestConstants.nipopowAlgos
8 | import org.ergoplatform.utils.generators.ErgoNodeGenerators.poPowProofGen
9 |
10 | class SerializationTests extends ErgoCorePropertyTest with org.ergoplatform.utils.SerializationTests {
11 | import org.ergoplatform.utils.generators.ErgoNodeWalletGenerators._
12 | import org.ergoplatform.utils.generators.ErgoCoreTransactionGenerators._
13 |
14 | property("Serializers should be defined for all block sections") {
15 | val block = invalidErgoFullBlockGen.sample.get
16 | block.toSeq.foreach { s =>
17 | ErgoNodeViewSynchronizer.modifierSerializers.get(s.modifierTypeId) should not be None
18 | }
19 | }
20 |
21 | property("WalletDigest serialization") {
22 | forAll(registrySummaryGen) { index =>
23 | WalletDigestSerializer.parseBytes(WalletDigestSerializer.toBytes(index)) shouldEqual index
24 | }
25 | }
26 |
27 | property("PoPowProof serialization") {
28 | checkSerializationRoundtrip(poPowProofGen, new NipopowProofSerializer(nipopowAlgos))
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/test/scala/org/ergoplatform/tools/DefaultParametersPrinter.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.tools
2 |
3 | import org.ergoplatform.settings.LaunchParameters.parametersTable
4 | import org.ergoplatform.settings.Parameters.{maxValues, minValues, parametersDescs, stepsTable}
5 |
6 | object DefaultParametersPrinter extends App {
7 | lazy val parametersDescription: String = {
8 | """
9 | |\begin{tabular}{*{6}{l}}
10 | |Id & Description & Default & Step & Min & Max \\
11 | |\hline
12 | """.stripMargin +
13 | parametersDescs.toSeq.sortBy(_._1).map { case (id, desc) =>
14 | val defaultOpt = parametersTable.get(id)
15 | val stepOpt = stepsTable.get(id)
16 | val minValue = minValues.get(id)
17 | val maxValue = maxValues.get(id)
18 | s"$id & $desc & ${defaultOpt.getOrElse("-")} & ${stepOpt.getOrElse("-")} & ${minValue.getOrElse("-")} & ${maxValue.getOrElse("-")} \\\\"
19 | }.mkString("\n") +
20 | """
21 | |\end{tabular}
22 | """.stripMargin
23 | }
24 |
25 | print(parametersDescription)
26 | }
27 |
--------------------------------------------------------------------------------
/src/test/scala/org/ergoplatform/tools/emissionPlot.gnu:
--------------------------------------------------------------------------------
1 | set print "-"
2 |
3 | set term png
4 | #set terminal png enhanced size 1280, 1024 font ',20'
5 | set terminal png enhanced font ',20'
6 | set xlabel "Time (years)" font ",20"
7 | set tics font ", 20"
8 | set key font ",20"
9 | set style line 1 lt 20 lw 4 linecolor 7
10 |
11 | set output "EmissionCurve.png"
12 | set ylabel "Coins total" font ",20"
13 |
14 | plot 'emission.csv' using 1:2 with lines ls 1 notitle
15 |
16 | set output "EmissionRate.png"
17 |
18 | set ylabel "Coins per block" font ",20"
19 |
20 | plot 'emission.csv' using 1:3 with lines ls 1 notitle
21 |
--------------------------------------------------------------------------------
/src/test/scala/org/ergoplatform/utils/NodeViewTestContext.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.utils
2 |
3 | import akka.actor.{ActorRef, ActorSystem}
4 | import akka.testkit.TestProbe
5 | import org.ergoplatform.settings.ErgoSettings
6 |
7 | trait NodeViewTestContext {
8 | def settings: ErgoSettings
9 | def actorSystem: ActorSystem
10 | def testProbe: TestProbe
11 | def nodeViewHolderRef: ActorRef
12 | }
13 |
--------------------------------------------------------------------------------
/src/test/scala/org/ergoplatform/utils/TestCase.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.utils
2 |
3 | import org.ergoplatform.settings.Parameters
4 | import org.ergoplatform.utils.fixtures.NodeViewFixture
5 |
6 | case class TestCase(name: String)(test: NodeViewFixture => Unit) {
7 | def run(parameters: Parameters, c: NodeViewTestConfig): Unit =
8 | new NodeViewFixture(c.toSettings, parameters).apply(test)
9 | }
10 |
--------------------------------------------------------------------------------
/src/test/scala/org/ergoplatform/utils/fixtures/SequentialAkkaFixture.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.utils.fixtures
2 |
3 | import java.util.concurrent.atomic.AtomicInteger
4 |
5 | import akka.actor.ActorSystem
6 | import akka.testkit.{TestKit, ImplicitSender}
7 | import org.scalatest.{Outcome, propspec}
8 |
9 | import scala.concurrent.Await
10 | import scala.concurrent.duration.Duration
11 |
12 | object SequentialAkkaFixture {
13 | val sysId = new AtomicInteger()
14 | }
15 |
16 | trait SequentialAkkaFixture extends propspec.FixtureAnyPropSpec {
17 | import SequentialAkkaFixture._
18 | type Fixture <: TestKit
19 | type FixtureParam = Fixture
20 |
21 | class AkkaFixture extends TestKit(ActorSystem("WithIsoFix-%d".format(sysId.incrementAndGet()))) with ImplicitSender
22 |
23 | def createAkkaFixture(): Fixture
24 |
25 | override def withFixture(test: OneArgTest): Outcome = {
26 | val sys = createAkkaFixture()
27 | try {
28 | test(sys)
29 | } finally {
30 | Await.result(sys.system.terminate(), Duration.Inf)
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/test/scala/org/ergoplatform/utils/fixtures/WalletFixture.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.utils.fixtures
2 |
3 | import org.ergoplatform.nodeView.wallet.ErgoWallet
4 | import org.ergoplatform.settings.{ErgoSettings, Parameters}
5 |
6 | class WalletFixture(
7 | settings: ErgoSettings,
8 | params: Parameters,
9 | getWallet: WalletFixture => ErgoWallet
10 | ) extends NodeViewFixture(settings, params) {
11 | val wallet: ErgoWallet = getWallet(this)
12 | }
13 |
--------------------------------------------------------------------------------
/src/test/scala/org/ergoplatform/utils/generators/ConnectedPeerGenerators.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.utils.generators
2 |
3 | import akka.actor.ActorRef
4 | import akka.util.ByteString
5 | import org.scalacheck.Gen
6 | import scorex.core.network._
7 | import org.ergoplatform.network.peer.PeerInfo
8 |
9 | object ConnectedPeerGenerators {
10 | import org.ergoplatform.utils.generators.CoreObjectGenerators._
11 |
12 | lazy val nonEmptyByteStringGen: Gen[ByteString] = nonEmptyBytesGen.map(ByteString(_))
13 |
14 | lazy val connectionIdGen: Gen[ConnectionId] = for {
15 | ip1 <- inetSocketAddressGen
16 | ip2 <- inetSocketAddressGen
17 | direction <- Gen.oneOf[ConnectionDirection](Seq[ConnectionDirection](Incoming, Outgoing))
18 | } yield ConnectionId(ip1, ip2, direction)
19 |
20 | def peerInfoGen: Gen[PeerInfo] = for {
21 | peerSpec <- peerSpecGen
22 | } yield PeerInfo(peerSpec, 0L, Some(Incoming), 0L)
23 |
24 | def connectedPeerGen(peerRef: ActorRef): Gen[ConnectedPeer] = for {
25 | connectionId <- connectionIdGen
26 | peerInfo <- peerInfoGen
27 | } yield ConnectedPeer(connectionId, peerRef, Some(peerInfo))
28 | }
29 |
--------------------------------------------------------------------------------
/src/test/scala/org/ergoplatform/utils/generators/ErgoNodeGenerators.scala:
--------------------------------------------------------------------------------
1 | package org.ergoplatform.utils.generators
2 |
3 | import org.ergoplatform.modifiers.history.popow.{NipopowProof, PoPowParams}
4 | import org.ergoplatform.nodeView.mempool.ErgoMemPool
5 | import org.ergoplatform.utils.ErgoCoreTestConstants.nipopowAlgos
6 | import org.scalacheck.{Arbitrary, Gen}
7 |
8 | object ErgoNodeGenerators {
9 | import org.ergoplatform.utils.ErgoNodeTestConstants._
10 | import org.ergoplatform.utils.generators.ChainGenerator._
11 |
12 | lazy val emptyMemPoolGen: Gen[ErgoMemPool] =
13 | Gen.resultOf({ _: Unit => ErgoMemPool.empty(settings) })(Arbitrary(Gen.const(())))
14 |
15 |
16 | lazy val poPowProofGen: Gen[NipopowProof] = for {
17 | m <- Gen.chooseNum(1, 128)
18 | k <- Gen.chooseNum(1, 128)
19 | proof <- validNiPoPowProofGen(m, k)
20 | } yield proof
21 |
22 | def validNiPoPowProofGen(m: Int, k: Int): Gen[NipopowProof] = for {
23 | mulM <- Gen.chooseNum(1, 20)
24 | } yield {
25 | val chain = genHeaderChain(m * mulM + k, diffBitsOpt = None, useRealTs = false)
26 | val popowChain = popowHeaderChain(chain)
27 | val params = PoPowParams(m, k, continuous = false)
28 | nipopowAlgos.prove(popowChain)(params).get
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/test/scala/scorex/core/network/PeerSpecSerializerSpec.scala:
--------------------------------------------------------------------------------
1 | package scorex.core.network
2 |
3 | import org.ergoplatform.network.PeerSpecSerializer
4 | import org.ergoplatform.utils.ErgoCorePropertyTest
5 | import scorex.util.ByteArrayBuilder
6 | import scorex.util.serialization.{VLQByteBufferReader, VLQByteBufferWriter}
7 |
8 | import java.nio.ByteBuffer
9 |
10 | class PeerSpecSerializerSpec extends ErgoCorePropertyTest {
11 | import org.ergoplatform.utils.generators.CoreObjectGenerators._
12 |
13 | property("All variants of peer spec should be serialized and deserialized successfully") {
14 | forAll(peerSpecGen) { peerSpec =>
15 | val writer = new VLQByteBufferWriter(new ByteArrayBuilder())
16 | PeerSpecSerializer.serialize(peerSpec, writer)
17 | val reader = new VLQByteBufferReader(ByteBuffer.wrap(writer.result().toBytes))
18 | val actualPeerSpec = PeerSpecSerializer.parse(reader)
19 | peerSpec shouldBe actualPeerSpec
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/test/scala/scorex/testkit/TestkitHelpers.scala:
--------------------------------------------------------------------------------
1 | package scorex.testkit
2 |
3 | trait TestkitHelpers {
4 |
5 | val MinTestsOk = 100
6 |
7 | def check(minTestsOk:Int)(f: Int => Unit): Unit = (0 until minTestsOk) foreach (i => f(i))
8 |
9 | def check(f: Int => Unit): Unit = check(MinTestsOk)(f)
10 | }
11 |
--------------------------------------------------------------------------------
/src/test/scala/scorex/testkit/generators/AllModifierProducers.scala:
--------------------------------------------------------------------------------
1 | package scorex.testkit.generators
2 |
3 | import org.ergoplatform.nodeView.state.ErgoState
4 |
5 | trait AllModifierProducers[ST <: ErgoState[ST]]
6 | extends SemanticallyValidModifierProducer[ST]
7 | with SyntacticallyTargetedModifierProducer
8 | with ArbitraryTransactionsCarryingModifierProducer
9 | with TotallyValidModifierProducer[ST]
10 | with SemanticallyValidTransactionsCarryingModifier[ST]
--------------------------------------------------------------------------------
/src/test/scala/scorex/testkit/generators/ArbitraryTransactionsCarryingModifierProducer.scala:
--------------------------------------------------------------------------------
1 | package scorex.testkit.generators
2 |
3 | import org.ergoplatform.modifiers.history.BlockTransactions
4 | import org.ergoplatform.modifiers.mempool.ErgoTransaction
5 | import org.ergoplatform.nodeView.mempool.ErgoMemPool
6 | /**
7 | * Produces a modifier with transactions, not necessary syntatically or semantically valid
8 | */
9 | trait ArbitraryTransactionsCarryingModifierProducer{
10 |
11 | def modifierWithTransactions(memoryPoolOpt: Option[ErgoMemPool], customTransactionsOpt: Option[Seq[ErgoTransaction]]): BlockTransactions
12 | }
13 |
--------------------------------------------------------------------------------
/src/test/scala/scorex/testkit/generators/CustomModifierProducer.scala:
--------------------------------------------------------------------------------
1 | package scorex.testkit.generators
2 |
3 | import org.ergoplatform.modifiers.BlockSection
4 | import org.ergoplatform.nodeView.history.ErgoHistory
5 | import org.ergoplatform.nodeView.state.ErgoState
6 |
7 | sealed trait ModifierProducerTemplateItem
8 |
9 | case object SynInvalid extends ModifierProducerTemplateItem
10 | case object Valid extends ModifierProducerTemplateItem
11 |
12 | trait CustomModifierProducer[ST <: ErgoState[ST]] {
13 |
14 | def customModifiers(history: ErgoHistory,
15 | state: ST,
16 | template: Seq[ModifierProducerTemplateItem]): Seq[BlockSection]
17 | }
18 |
--------------------------------------------------------------------------------
/src/test/scala/scorex/testkit/generators/SemanticallyInvalidModifierProducer.scala:
--------------------------------------------------------------------------------
1 | package scorex.testkit.generators
2 |
3 | import org.ergoplatform.modifiers.BlockSection
4 | import org.ergoplatform.nodeView.state.ErgoState
5 |
6 |
7 | trait SemanticallyInvalidModifierProducer[ST <: ErgoState[ST]] {
8 | def semanticallyInvalidModifier(state: ST): BlockSection
9 | }
10 |
--------------------------------------------------------------------------------
/src/test/scala/scorex/testkit/generators/SemanticallyValidModifierProducer.scala:
--------------------------------------------------------------------------------
1 | package scorex.testkit.generators
2 |
3 | import org.ergoplatform.modifiers.BlockSection
4 | import org.ergoplatform.nodeView.state.ErgoState
5 |
6 | trait SemanticallyValidModifierProducer[ST <: ErgoState[ST]] {
7 | def semanticallyValidModifier(state: ST): BlockSection
8 | }
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/test/scala/scorex/testkit/generators/SemanticallyValidTransactionsCarryingModifier.scala:
--------------------------------------------------------------------------------
1 | package scorex.testkit.generators
2 |
3 | import org.ergoplatform.modifiers.history.BlockTransactions
4 | import org.ergoplatform.modifiers.mempool.ErgoTransaction
5 | import org.ergoplatform.nodeView.state.ErgoState
6 |
7 | trait SemanticallyValidTransactionsCarryingModifier[ST <: ErgoState[ST]] {
8 |
9 | def semanticallyValidModifier(state: ST): BlockTransactions
10 | def genValidTransactionPair(state: ST): Seq[ErgoTransaction]
11 | def semanticallyValidModifierWithCustomTransactions(state: ST, transactions: Seq[ErgoTransaction]): BlockTransactions
12 | }
13 |
--------------------------------------------------------------------------------
/src/test/scala/scorex/testkit/generators/SyntacticallyTargetedModifierProducer.scala:
--------------------------------------------------------------------------------
1 | package scorex.testkit.generators
2 |
3 | import org.ergoplatform.modifiers.BlockSection
4 | import org.ergoplatform.nodeView.history.ErgoHistory
5 |
6 |
7 | trait SyntacticallyTargetedModifierProducer {
8 | def syntacticallyValidModifier(history: ErgoHistory): BlockSection
9 |
10 | def syntacticallyInvalidModifier(history: ErgoHistory): BlockSection
11 | }
12 |
--------------------------------------------------------------------------------
/src/test/scala/scorex/testkit/generators/TotallyValidModifierProducer.scala:
--------------------------------------------------------------------------------
1 | package scorex.testkit.generators
2 |
3 | import org.ergoplatform.modifiers.BlockSection
4 | import org.ergoplatform.nodeView.history.ErgoHistory
5 | import org.ergoplatform.nodeView.state.ErgoState
6 |
7 |
8 | trait TotallyValidModifierProducer[ST <: ErgoState[ST]] {
9 |
10 | def totallyValidModifier(history: ErgoHistory, state: ST): BlockSection
11 |
12 | def totallyValidModifiers(history: ErgoHistory, state: ST, count: Int): Seq[BlockSection]
13 | }
14 |
--------------------------------------------------------------------------------
/src/test/scala/scorex/testkit/properties/mempool/MemoryPoolTest.scala:
--------------------------------------------------------------------------------
1 | package scorex.testkit.properties.mempool
2 |
3 | import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction}
4 | import org.ergoplatform.nodeView.mempool.ErgoMemPool
5 | import org.scalacheck.Gen
6 |
7 |
8 | trait MemoryPoolTest {
9 | val memPool: ErgoMemPool
10 | val memPoolGenerator: Gen[ErgoMemPool]
11 | val transactionGenerator: Gen[ErgoTransaction]
12 | val unconfirmedTxGenerator: Gen[UnconfirmedTransaction]
13 | }
14 |
--------------------------------------------------------------------------------
/src/test/scala/scorex/testkit/properties/state/StateTests.scala:
--------------------------------------------------------------------------------
1 | package scorex.testkit.properties.state
2 |
3 | import org.ergoplatform.nodeView.state.ErgoState
4 | import scorex.testkit.{TestkitHelpers, generators}
5 | import org.scalacheck.Gen
6 | import org.scalatest.matchers.should.Matchers
7 | import org.scalatest.propspec.AnyPropSpec
8 | import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
9 |
10 | trait StateTests[ST <: ErgoState[ST]]
11 | extends AnyPropSpec
12 | with ScalaCheckPropertyChecks
13 | with Matchers
14 | with TestkitHelpers
15 | with generators.SemanticallyValidModifierProducer[ST]
16 | with generators.SemanticallyInvalidModifierProducer[ST] {
17 |
18 | val checksToMake = 10
19 |
20 | val stateGen: Gen[ST]
21 | }
22 |
--------------------------------------------------------------------------------
/src/test/scala/scorex/testkit/utils/AkkaFixture.scala:
--------------------------------------------------------------------------------
1 | package scorex.testkit.utils
2 |
3 | import java.util.concurrent.atomic.AtomicInteger
4 | import akka.actor.ActorSystem
5 | import akka.testkit.{ImplicitSender, TestKit}
6 |
7 | object SysId {
8 | private val i = new AtomicInteger()
9 | def incrementAndGet(): Int = i.incrementAndGet()
10 | }
11 |
12 | class AkkaFixture
13 | extends TestKit(ActorSystem("WithIsoFix-%d".format(SysId.incrementAndGet())))
14 | with ImplicitSender
15 |
--------------------------------------------------------------------------------