├── .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 | 4 | 5 | 6 | 7 | 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 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/main/resources/panel/static/media/copy.icon.835ebda7.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/main/resources/panel/static/media/redo-arrow-symbol.e801de31.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/main/resources/panel/static/media/remove.94c0849a.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 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 | --------------------------------------------------------------------------------